[
  {
    "path": ".gitignore",
    "content": "*.pyc\npython/dirigible/dirigible.db\n"
  },
  {
    "path": "LICENSE.md",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2014 Resolver Systems Ltd, PythonAnywhere LLP\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n\n"
  },
  {
    "path": "README.md",
    "content": "Dirigible, the web-based Pythonic Spreadsheet\n=============================================\n\nThis is the source code from the end-of-lifed https://www.projectdirigible.com project, preserved for posterity and the curious\n\n\nInstallation instructions\n-------------------------\n\n    cd dirigible\n    pip install -r requirements.txt\n    python manage.py migrate\n    python manage.py createsuperuser  # make yourself a user account.\n    python manage.py runserver\n\nAnd visit http://localhost:8000\n\nSecurity\n--------\n\nThis version of Dirigible has absolutely no security, so bewarned, before you put this on a public server -- anyone that logs in has the full access permissions of whichever user is running django, and you can use Python to do pretty much whatever you want!\n\nCheck out the chroot_recalculation branch for a slightly more secure approach\n\n\nSome minimal context\n--------------------\n\n(Probably best to play around with the spreadsheet a bit before reading this guide, to get an idea of what is possible.  Try creating a couple of formulae with calculations, and maybe using a user-defined function from the usercode panel)\n\n* A Dirigible spreadsheet is \"just\" a python program, which is visible in the usercode panel on the right.  \n\n* Recalculating the spreadsheet means executing that code, including two very important built-in functions:  `load_constants` and `evaluate_formulae`.  \n\nIn between those two functions, the user can add their own arbitrary code. \n\nOne global object is accessible, the `worksheet` ([source](https://github.com/pythonanywhere/dirigible-spreadsheet/blob/master/dirigible/sheet/worksheet.py))\n\nA worksheet is compose of cells (it is in fact a dictionary, whose keys are the location, expressed as a tuple of column,row, and whose values are cell objects)\n\nA cell ([source](https://github.com/pythonanywhere/dirigible-spreadsheet/blob/master/dirigible/sheet/cell.py)) has two key attributes:\n\n- its `formula`, which may just be a constant like \"hello\" or \"12,3\", or a formula, like =A1+B1\n- its `value`, which is the result of evaluating the formula.  \n\nIf the formula is a constant, its value is that constant.\n\nIf it's a real formula, then it will be evaluated as part of `evaluate_formulae()`.  This involves:\n\n1. Parsing the formula ([source](https://github.com/pythonanywhere/dirigible-spreadsheet/blob/master/dirigible/sheet/formula_interpreter.py))\n    - cell formulae can include any valid python, as well as\n    - special spreadsheet syntax, including cell references like A1 or B2, and special spreadsheet formulae like the SUM function\n    - any special spreadsheet syntax is parsed and converted to Python\n    - and finding out the `dependencies`.  If the formula for cell A1 includes a reference to B1, then B1 is a dependency of A1\n\n2. Placing it into the whole spreadsheet's dependency graph ([source](https://github.com/pythonanywhere/dirigible-spreadsheet/blob/master/dirigible/sheet/dependency_graph.py))\n\n3. Evaluating all the branches of that graph, starting from its leaves, by evaluating the cell's formula to get its value.  That can then be fed into cells that depend on it, and so on.  (See [calculate.py](https://github.com/pythonanywhere/dirigible-spreadsheet/blob/master/dirigible/sheet/calculate.py))\n\nCell formulae can also use any user-defined functions from the usercode.\n\n\nEverything else is fairly peripheral.  There is some monkeying around with json, some stuff with threads that's a throwback to some parallelisation features that I haven't finished removing.\n\nQuestions and requests for clarification are solicited, as are any pull requests for documentation, bugfixes, fixes to unit tests (of which all but three were passing in this modified version of our codebase, at the time of writing), or FTs (half-ported from old selenium)\n\n"
  },
  {
    "path": "dirigible/.gitignore",
    "content": "db.sqlite3\nfts/screendumps\n"
  },
  {
    "path": "dirigible/dirigible/__init__.py",
    "content": ""
  },
  {
    "path": "dirigible/dirigible/settings.py",
    "content": "\"\"\"\nDjango settings for dirigible project.\n\nFor more information on this file, see\nhttps://docs.djangoproject.com/en/dev/topics/settings/\n\nFor the full list of settings and their values, see\nhttps://docs.djangoproject.com/en/dev/ref/settings/\n\"\"\"\n\n# Build paths inside the project like this: os.path.join(BASE_DIR, ...)\nimport os\nBASE_DIR = os.path.dirname(os.path.dirname(__file__))\n\n\n# Quick-start development settings - unsuitable for production\n# See https://docs.djangoproject.com/en/dev/howto/deployment/checklist/\n\n# SECURITY WARNING: keep the secret key used in production secret!\nSECRET_KEY = '0590fte(p#@fk+ii_-vfzwuixst9z)nqzszr!wx7pkjv++sa%2'\n\n# SECURITY WARNING: don't run with debug turned on in production!\nDEBUG = True\n\nTEMPLATE_DEBUG = True\n\nALLOWED_HOSTS = []\n\n\n# Application definition\n\nINSTALLED_APPS = (\n    'django.contrib.admin',\n    'django.contrib.auth',\n    'django.contrib.contenttypes',\n    'django.contrib.sessions',\n    'django.contrib.messages',\n    'django.contrib.staticfiles',\n    'registration',\n    'user',\n    'feedback',\n    'info_pages',\n    'featured_sheet',\n    'sheet',\n    'shared',\n)\n\nMIDDLEWARE_CLASSES = (\n    'django.contrib.sessions.middleware.SessionMiddleware',\n    'django.middleware.common.CommonMiddleware',\n    'django.middleware.csrf.CsrfViewMiddleware',\n    'django.contrib.auth.middleware.AuthenticationMiddleware',\n    'django.contrib.auth.middleware.SessionAuthenticationMiddleware',\n    'django.contrib.messages.middleware.MessageMiddleware',\n    'django.middleware.clickjacking.XFrameOptionsMiddleware',\n)\n\nROOT_URLCONF = 'dirigible.urls'\n\nWSGI_APPLICATION = 'dirigible.wsgi.application'\n\n\n# Database\n# https://docs.djangoproject.com/en/dev/ref/settings/#databases\n\nDATABASES = {\n    'default': {\n        'ENGINE': 'django.db.backends.sqlite3',\n        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),\n    }\n}\n\n# Internationalization\n# https://docs.djangoproject.com/en/dev/topics/i18n/\n\nLANGUAGE_CODE = 'en-us'\n\nTIME_ZONE = 'UTC'\n\nUSE_I18N = True\n\nUSE_L10N = True\n\nUSE_TZ = False\n\n\n# Static files (CSS, JavaScript, Images)\n# https://docs.djangoproject.com/en/dev/howto/static-files/\n\nSTATIC_URL = '/static/'\n\nLOGIN_URL = '/login'\nLOGIN_REDIRECT_URL = '/'\n\nFEEDBACK_EMAIL = 'support@example.com'\n"
  },
  {
    "path": "dirigible/dirigible/test_utils.py",
    "content": "# Copyright (c) 2005-2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\ntry:\n    import unittest2 as unittest\nexcept ImportError:\n    import unittest\n\nfrom os.path import dirname, join\nimport sys\n\nimport django\n\n\ndef create_suite_for_file_directory(file):\n    def suite():\n        start_dir = dirname(file)\n        return unittest.defaultTestLoader.discover(\n            start_dir,\n            top_level_dir=join(dirname(__file__), \"..\")\n        )\n    return suite\n\n\ndef die(exception=None):\n    if exception is None:\n        exception = AssertionError('die called')\n    def inner_die(*_):\n        raise exception\n    return inner_die\n\n\n\nclass ResolverTestMixins(object):\n    def assertCalledOnce(self, mock, *args, **kwargs):\n        if mock.call_args_list == []:\n            self.fail('Not called')\n        self.assertEquals(mock.call_args_list, [(args, kwargs)])\n\n\n\nclass ResolverTestCase(unittest.TestCase, ResolverTestMixins):\n    maxDiff = None\n\n\n\nclass ResolverDjangoTestCase(django.test.TestCase, ResolverTestMixins):\n    maxDiff = None\n\n\n\nTEST_CLASSES = (\n    unittest.TestCase,\n    django.test.TestCase,\n    django.test.TransactionTestCase,\n    ResolverTestCase,\n    ResolverDjangoTestCase\n)\n\ndef assert_security_classes_exist(test, module_name, excludes=None):\n    '''\n    ensure that, as a minimum sanity check, each non-security test class in\n    this module has an associated security test class.\n    '''\n    test_classes = [\n        name for name, item in sys.modules[module_name].__dict__.iteritems()\n        if isinstance(item, type) and issubclass(item, TEST_CLASSES)\n        and not item in TEST_CLASSES\n    ]\n    regular_test_classes = [\n        name for name in test_classes\n        if not name.endswith('SecurityTest')\n    ]\n    if excludes is None:\n        excludes = []\n    for name in regular_test_classes:\n        if name not in excludes:\n            test.assertTrue(\n                name[:-4] + 'SecurityTest' in test_classes,\n                \"class %s doesn't have a security test. \"\n                \"Use user page security test as template\" % (name,)\n            )\n\n"
  },
  {
    "path": "dirigible/dirigible/urls.py",
    "content": "import os\n\nfrom django.conf.urls import patterns, include, url\nfrom django.contrib import admin\nfrom django.conf import settings\nfrom django.views.generic import TemplateView\n\nfrom info_pages.views import front_page_view, info_page_view\nfrom sheet.views import new_sheet\n\n\nurlpatterns = patterns(\n    '',\n\n    url(\n        r'^$',\n        front_page_view,\n        name='front_page'\n    ),\n\n    url(\n        r'^(?P<template_name>oss|video)/',\n        info_page_view,\n        name=\"info_page\"\n    ),\n\n    url(\n        # If you change this, don't forget to change the LOGIN_URL in settings.py\n        # Here be dragons. The settings .py one has no trailing slash and needs to\n        # stay that way. Changing either of these will stop it from working in Chrome\n        # and in Firefox if you press ENTER to login.\n        r'^login/',\n        'django.contrib.auth.views.login',\n        {'template_name': 'login.html'},\n        name=\"login\"\n    ),\n\n    url(\n        r'^logout$',\n        'django.contrib.auth.views.logout',\n        {'next_page': settings.LOGIN_URL},\n        name=\"logout\"\n    ),\n\n    url(\n        r'^new_sheet$',\n        new_sheet,\n        name=\"new_sheet\"\n    ),\n\n    url(\n        r'^user/',\n        include('user.urls')\n    ),\n\n    url(\n        r'^signup/',\n        include('user.signup_urls')\n    ),\n\n\n    url(\n        r'^feedback/',\n        include('feedback.urls')\n    ),\n\n\n    url(\n        r'^featured_sheets/$',\n        TemplateView.as_view(template_name='featured_sheets.html'),#, context={'sheets': FeaturedSheet.objects.all}),\n        name='featured_sheets'\n    ),\n\n    url(r'^admin/', include(admin.site.urls)),\n\n)\n"
  },
  {
    "path": "dirigible/dirigible/wsgi.py",
    "content": "\"\"\"\nWSGI config for dirigible project.\n\nIt exposes the WSGI callable as a module-level variable named ``application``.\n\nFor more information on this file, see\nhttps://docs.djangoproject.com/en/dev/howto/deployment/wsgi/\n\"\"\"\n\nimport os\nos.environ.setdefault(\"DJANGO_SETTINGS_MODULE\", \"dirigible.settings\")\n\nfrom django.core.wsgi import get_wsgi_application\napplication = get_wsgi_application()\n"
  },
  {
    "path": "dirigible/featured_sheet/__init__.py",
    "content": ""
  },
  {
    "path": "dirigible/featured_sheet/admin.py",
    "content": "# Copyright (c) 2011 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\nfrom featured_sheet.models import FeaturedSheet\nfrom django.contrib import admin\n\nadmin.site.register(FeaturedSheet)\n\n"
  },
  {
    "path": "dirigible/featured_sheet/models.py",
    "content": "# Copyright (c) 2011 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\nfrom django.db import models\n\nfrom sheet.models import Sheet\n\nclass FeaturedSheet(models.Model):\n    sheet = models.ForeignKey(Sheet)\n    description = models.TextField()\n    more_info_url = models.CharField(max_length=1024, default='', blank=True)\n\n    def __unicode__(self):\n        return 'Feature: %s' % (self.sheet.name,)\n"
  },
  {
    "path": "dirigible/featured_sheet/templates/featured_sheets.html",
    "content": "{% extends \"info_page.html\" %}\n\n{% block title %}\n    Featured Sheets: Dirigible\n{% endblock %}\n\n{% block head %}\n    {{ block.super }}\n    <link rel=\"stylesheet\" href=\"/static/dirigible/styles/info_page.css\" type=\"text/css\" media=\"screen\" charset=\"utf-8\" />\n{% endblock %}\n\n{% block middle %}\n\n  <h1>Featured sheets</h1>\n\n    {% for sheet in sheets %}\n      <div class=\"{{sheet.sheet.id}}\">\n      <div class=\"featured_sheet_actions\">\n          {% if sheet.more_info_url %}\n          <span class=\"featured_sheet_more_info\">\n              <a href=\"{{sheet.more_info_url}}\">More info</a>\n          </span>\n          {% endif %}\n          <span class=\"featured_sheet_view_link\">\n              <a  href=\"{% url sheet_page sheet.sheet.owner.username sheet.sheet.id %}\">View sheet</a>\n          </span>\n          <span class=\"featured_sheet_copy_link\">\n              <a  href=\"{% url sheet_copy_sheet sheet.sheet.owner.username sheet.sheet.id %}\">Get a copy</a>\n          </span>\n      </div>\n      <h2 class=\"featured_sheet_name\">\n          <a href=\"{% url sheet_page sheet.sheet.owner.username sheet.sheet.id %}\">{{sheet.sheet.name}}</a>\n      </h2>\n      <div class=\"featured_sheet_description\">{{sheet.description}}</div>\n      </div>\n\n        {% endfor %}\n\n{% endblock %}\n"
  },
  {
    "path": "dirigible/featured_sheet/tests/__init__.py",
    "content": "\n"
  },
  {
    "path": "dirigible/featured_sheet/tests/test_models.py",
    "content": "# Copyright (c) 2011 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\nfrom django.contrib.auth.models import User\n\nfrom dirigible.test_utils import ResolverTestCase\nfrom sheet.models import Sheet\n\nfrom featured_sheet.models import FeaturedSheet\n\n\nclass TestFeaturedSheetModel(ResolverTestCase):\n\n    def test_can_construct_without_more_info_url(self):\n        user = User(username='featurer')\n        user.save()\n        sheet = Sheet(owner=user, name='sheet to feature')\n        sheet.save()\n\n        description = 'twas brillig and the slithy toves'\n        fs = FeaturedSheet(sheet=sheet, description=description)\n        fs.save()\n\n        self.assertEquals(fs.sheet, sheet)\n        self.assertEquals(fs.description, description)\n        self.assertEquals(fs.more_info_url, '')\n\n\n    def test_can_construct_with_more_info_url(self):\n        user = User(username='chattyfeaturer')\n        user.save()\n        sheet = Sheet(owner=user, name='sheet to feature')\n        sheet.save()\n\n        description = 'twas brillig and the slithy toves'\n        more_info_url = 'http://far.away/'\n        fs = FeaturedSheet(sheet=sheet, description=description, more_info_url=more_info_url)\n        fs.save()\n\n        self.assertEquals(fs.sheet, sheet)\n        self.assertEquals(fs.description, description)\n        self.assertEquals(fs.more_info_url, more_info_url)\n\n\n    def test_unicode(self):\n        user = User(username='printyfeaturer')\n        user.save()\n        sheet = Sheet(owner=user, name='sheet to feature')\n        sheet.save()\n\n        description = 'twas brillig and the slithy toves'\n        more_info_url = 'http://far.away/'\n        fs = FeaturedSheet(sheet=sheet, description=description, more_info_url=more_info_url)\n        fs.save()\n\n        self.assertEquals(unicode(fs), u'Feature: %s' % (sheet.name,))\n\n"
  },
  {
    "path": "dirigible/featured_sheet/views.py",
    "content": "# Copyright (c) 2011 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\n"
  },
  {
    "path": "dirigible/feedback/__init__.py",
    "content": ""
  },
  {
    "path": "dirigible/feedback/models.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\nfrom django.db import models\n"
  },
  {
    "path": "dirigible/feedback/tests/__init__.py",
    "content": "\n"
  },
  {
    "path": "dirigible/feedback/tests/test_views.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\nfrom mock import patch\nfrom textwrap import dedent\n\nfrom django.conf import settings\nfrom django.http import HttpRequest, HttpResponse\n\nfrom dirigible.test_utils import ResolverTestCase\n\nfrom feedback.views import submit\n\n\nclass SubmitTest(ResolverTestCase):\n\n    @patch('feedback.views.send_mail')\n    def test_submit_with_message_and_email_address_and_username_sends_admin_email_with_all_three(self, mock_send_mail):\n        request = HttpRequest()\n        request.POST[\"message\"] = \"a test message\"\n        request.POST[\"email_address\"] = \"a test email address\"\n        request.POST[\"username\"] = \"a test username\"\n        request.META['HTTP_REFERER'] = 'a test page'\n\n        response = submit(request)\n\n        self.assertTrue(isinstance(response, HttpResponse))\n        self.assertEquals(response.content, \"OK\")\n\n        self.assertCalledOnce(\n            mock_send_mail,\n            \"User feedback from Dirigible\",\n            dedent(\"\"\"\n                Username: a test username\n                Email address: a test email address\n                Page: a test page\n\n                Message:\n                a test message\n            \"\"\"),\n            settings.SERVER_EMAIL,\n            [settings.FEEDBACK_EMAIL]\n        )\n"
  },
  {
    "path": "dirigible/feedback/urls.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\nfrom django.conf.urls import *\n\nfrom feedback.views import submit\n\n\nurlpatterns = patterns('',\n\n    url(\n        r'^submit/$',\n        submit,\n        name=\"feedback_submit\"\n    ),\n\n)\n"
  },
  {
    "path": "dirigible/feedback/views.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\nfrom textwrap import dedent\n\nfrom django.core.mail import send_mail\nfrom django.http import HttpResponse\nfrom django.conf import settings\n\n\n\ndef submit(request):\n    send_mail(\n        \"User feedback from Dirigible\",\n        dedent(\"\"\"\n            Username: %s\n            Email address: %s\n            Page: %s\n\n            Message:\n            %s\n        \"\"\") % (\n            request.POST[\"username\"], request.POST[\"email_address\"],\n            request.META['HTTP_REFERER'], request.POST[\"message\"]\n        ),\n        settings.SERVER_EMAIL,\n        [settings.FEEDBACK_EMAIL]\n    )\n    return HttpResponse(\"OK\")\n"
  },
  {
    "path": "dirigible/fts/__init__.py",
    "content": ""
  },
  {
    "path": "dirigible/fts/screendumps/placeholder",
    "content": ""
  },
  {
    "path": "dirigible/fts/tests/__init__.py",
    "content": ""
  },
  {
    "path": "dirigible/fts/tests/functionaltest.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\nfrom __future__ import print_function\n\nfrom contextlib import contextmanager\nfrom email.parser import Parser\nfrom functools import wraps\nfrom textwrap import dedent\nfrom threading import Thread\nfrom urlparse import urljoin, urlparse, urlunparse\nimport datetime\nimport hashlib\nimport os\nimport re\nimport time\nimport urllib\nimport urllib2\n\nfrom django.conf import settings\nfrom django.contrib.auth import BACKEND_SESSION_KEY, SESSION_KEY, HASH_SESSION_KEY\nfrom django.contrib.auth.models import User\nfrom django.contrib.sessions.backends.db import SessionStore\nfrom django.contrib.staticfiles.testing import StaticLiveServerTestCase\n\nfrom selenium import webdriver\nfrom selenium.common.exceptions import NoSuchElementException\nfrom selenium.webdriver.common.action_chains import ActionChains\nfrom selenium.webdriver.common.keys import Keys\n\nfrom sheet.sheet import Sheet\n\nUSER_PASSWORD = 'p4ssw0rd'\n\nDEFAULT_WAIT_FOR_TIMEOUT = 2\nDEFAULT_TYPING_WAIT = 0.1\n\nCURRENT_API_VERSION = '0.1'\nSCREEN_DUMP_LOCATION = os.path.abspath(\n    os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'screendumps')\n)\nIMAP_HOST = \"\"\nIMAP_USERNAME = \"\"\nIMAP_PASSWORD = \"\"\n\n\ndef _debug(text):\n    msg = '{}   {}'.format(round(time.time(), 2), text)\n    print(msg)\n    # print(msg, file=sys.stderr)\n\n\nclass Url(object):\n    ROOT = 'http://localhost:8081/'\n    LOGIN = urljoin(ROOT, '/login/')\n    LOGOUT = urljoin(ROOT, '/logout')\n    NEW_SHEET = urljoin(ROOT, '/new_sheet')\n    SIGNUP = urljoin(ROOT, '/signup/register/')\n    DOCUMENTATION = urljoin(ROOT, '/documentation/')\n    API_DOCS = urljoin(DOCUMENTATION, 'builtins.html')\n\n\n    @classmethod\n    def user_page(cls, username):\n        return urljoin(Url.ROOT, '/user/%s/' % (username,))\n\n    @classmethod\n    def sheet_page(cls, username, sheet_id):\n        return urljoin(cls.user_page(username), 'sheet/%s/' % (sheet_id,))\n\n    @classmethod\n    def api_url(cls, username, sheet_id):\n        return urljoin(cls.sheet_page(username, sheet_id), 'v%s/json/' % (CURRENT_API_VERSION,))\n\n\n\ndef snapshot_on_error(test):\n\n    @wraps(test)\n    def inner(*args, **kwargs):\n        try:\n            test(*args, **kwargs)\n        except:\n            test_object = args[0]\n\n            try:\n                filename = test_object.get_dump_filename()\n                _debug('screenshot to {}.png'.format(filename))\n                test_object.browser.get_screenshot_as_file(filename + '.png')\n                _debug('page source dump  to {}.html'.format(filename))\n                with open(filename + '.html', 'w') as f:\n                    f.write(test_object.browser.page_source.encode('utf8'))\n                _debug('page text dump  to {}.txt'.format(filename))\n                with open(filename + '.txt', 'w') as f:\n                    body_text = test_object.browser.find_element_by_tag_name('body').text\n                    f.write(body_text.encode('utf8'))\n            except:\n                _debug('Exception writing screenshots')\n            raise\n    return inner\n\n\ndef humanesque_delay(length=DEFAULT_TYPING_WAIT):\n    time.sleep(length)\n\n\ndef humanise_with_delay(action):\n    @wraps(action)\n    def inner(*args, **kwargs):\n        humanesque_delay()\n        result = action(*args, **kwargs)\n        humanesque_delay()\n        return result\n    return inner\n\n\nclass Bounds(object):\n    def __init__(self, width, height, top, left):\n        self.width = width\n        self.height = height\n        self.top = top\n        self.left = left\n\n    bottom = property(lambda self: self.top + self.height)\n\n    right = property(lambda self: self.left + self.width)\n\n\nRGB_RE = re.compile('^rgba?\\((\\d+), (\\d+), (\\d+)(, (\\d+))?\\)')\n\ndef convert_rgb_to_hex(value):\n    match = RGB_RE.match(value)\n    r, g, b = match.group(1), match.group(2), match.group(3)\n    return '#%X%X%X' % (int(r), int(g), int(b))\n\n\nclass FunctionalTest(StaticLiveServerTestCase):\n    user_count = 1\n\n    def wait_for(\n        self, condition_function, msg_function,\n        timeout_seconds=DEFAULT_WAIT_FOR_TIMEOUT, allow_exceptions=False\n    ):\n        start = time.time()\n        end = start + timeout_seconds\n        exception_raised = False\n        tries = 0\n        while tries < 2 or time.time() < end:\n            _debug('Waiting for {}'.format(msg_function()[:30]))\n            try:\n                tries += 1\n                if condition_function():\n                    return\n                exception_raised = False\n            except Exception, e:\n                if not allow_exceptions:\n                    raise e\n                exception_raised = True\n            time.sleep(0.1)\n        if exception_raised:\n            raise\n        self.fail(\"Timeout waiting for condition: %s\" % (msg_function(),))\n\n    def get_dump_filename(self):\n        timestamp = datetime.datetime.now().isoformat().replace(':', '.')[:19]\n        return '{folder}/{test_id}-{timestamp}'.format(\n            folder=SCREEN_DUMP_LOCATION,\n            test_id=self.id(),\n            timestamp=timestamp\n        )\n\n    session_keys = {}\n\n    def create_users(self):\n        for username in self.get_my_usernames():\n            user = User.objects.create(username=username)\n            user.set_password('p4ssw0rd')\n            user.save()\n            profile = user.get_profile()\n            profile.has_seen_sheet_page = True\n            profile.save()\n\n            # create sessions we can use for login too\n            session = SessionStore()\n            session[SESSION_KEY] = user.pk\n            session[BACKEND_SESSION_KEY] = settings.AUTHENTICATION_BACKENDS[0]\n            session[HASH_SESSION_KEY] = user.get_session_auth_hash()\n            session.save()\n            self.session_keys[username] = session.session_key\n\n\n\n    def setUp(self):\n        self.create_users()\n        _debug(\"%s ##### Running test %s\" % (datetime.datetime.now(), self.id()))\n        self.browser = webdriver.Firefox()\n        self.browser.implicitly_wait(DEFAULT_WAIT_FOR_TIMEOUT)\n        self.browser.set_window_size(1024, 768)\n\n\n    def tearDown(self):\n        _debug('quitting browser')\n        self.browser.quit()\n        _debug(\"%s ##### Finished test %s\" % (datetime.datetime.now(), self.id()))\n\n\n    def login(\n        self, username=None, password=USER_PASSWORD, manually=False\n    ):\n        if username is None:\n            username = self.get_my_username()\n\n        if manually:\n            self.get_element('id=id_username').clear()\n            self.get_element('id=id_password').clear()\n            self.get_element('id=id_username').send_keys(username)\n            self.get_element('id=id_password').send_keys(password)\n            self.click_link('id_login')\n            return\n\n        session_key = self.session_keys[username]\n        ## to set a cookie we need to first visit the domain.\n        ## 404 pages load the quickest!\n        self.browser.get(urljoin(Url.ROOT, \"/404_no_such_url/\"))\n        self.browser.add_cookie(dict(\n            name=settings.SESSION_COOKIE_NAME,\n            value=session_key,\n            path='/',\n        ))\n        self.go_to_url(Url.ROOT)\n\n\n    def logout(self):\n        self.go_to_url(Url.LOGOUT)\n\n\n    def get_element(self, locator):\n        if locator.startswith('css='):\n            return self.browser.find_element_by_css_selector(locator[4:])\n        elif locator.startswith('id='):\n            return self.browser.find_element_by_id(locator[3:])\n\n\n\n    def is_element_focused(self, locator):\n        element = self.get_element(locator)\n        focused_element = self.browser.switch_to_active_element()\n        return element == focused_element\n\n\n    def is_element_present(self, locator):\n        try:\n            self.get_element(locator)\n            return True\n        except NoSuchElementException:\n            return False\n\n\n    def get_text(self, locator):\n        return self.get_element(locator).text\n\n\n    def get_value(self, locator):\n        return self.get_element(locator).get_attribute('value')\n\n\n    def human_key_press(self, key_code):\n        _debug('pressing key %r' % (key_code,))\n        self.browser.switch_to_active_element().send_keys(key_code)\n\n\n    @contextmanager\n    def key_down(self, key_code):\n        _debug('key down %r' % (key_code,))\n        self.browser.switch_to_active_element().send_keys(key_code)\n        ActionChains(self.browser).key_down(key_code).perform()\n        yield\n        # apparently there's no need for a key up??\n        # ActionChains(self.browser).key_up(key_code).perform()\n\n\n    def click_to_and_blur_from(self, click_to_locator, blur_from_locator):\n        self.selenium.fire_event(blur_from_locator, 'blur')\n        self.selenium.click(click_to_locator)\n\n\n    def get_element_bounds(self, locator):\n        return Bounds(\n            self.selenium.get_element_width(locator),\n            self.selenium.get_element_height(locator),\n            self.selenium.get_element_position_top(locator),\n            self.selenium.get_element_position_left(locator)\n        )\n\n\n    def get_css_property(self, jquery_locator, property_name):\n        property_value = self.selenium.get_eval('window.$(\"%s\").css(\"%s\")' % (jquery_locator, property_name))\n        if property_value == 'rgba(0, 0, 0, 0)': # transparent in chrome\n            return 'transparent'\n        if property_value.startswith('rgb'):\n            property_value = convert_rgb_to_hex(property_value)\n        if property_value.startswith('#'):\n            property_value = property_value.upper()\n            if len(property_value) == 4:\n                _, r, g, b = property_value\n                property_value = '#%s%s%s%s%s%s' % (r, r, g, g, b, b)\n        return property_value\n\n\n    def assert_urls_are_same(self, actual, expected):\n        loc = self.browser.current_url\n        canonicalised_actual = urljoin(loc, actual)\n        canonicalised_expected = urljoin(loc, expected)\n        self.assertEquals(canonicalised_actual, canonicalised_expected)\n\n\n    def assert_HTTP_error(self, url, error_code):\n        self.browser.get(url)\n        possible_error_locators = ('id=summary', 'id=id_server_error_title')\n        for error_locator in possible_error_locators:\n            if self.is_element_present(error_locator) and str(error_code) in self.get_text(error_locator):\n                return\n        self.fail('%d not raised, got: %s' % (error_code, self.browser.title))\n\n\n    def assert_redirects(self, from_url, to_url):\n        self.go_to_url(from_url)\n        self.assert_urls_are_same(\n            urlunparse(urlparse(self.browser.current_url)[:4] + ('', '')),\n            to_url\n        )\n\n    def is_element_enabled(self, element_id):\n        #self.selenium.get_attribute is unreliable (Harry, Jonathan)\n        disabled = self.selenium.get_eval('window.$(\"#%s\").attr(\"disabled\")' % (element_id,))\n        return disabled not in (\"true\", \"disabled\")\n\n\n    def wait_for_element_presence(\n        self, locator, present=True, timeout_seconds=DEFAULT_WAIT_FOR_TIMEOUT\n    ):\n        if present:\n            failure_message = \"Element %s to be present\" % (locator, ),\n        else:\n            failure_message = \"Element %s to not exist\" % (locator, ),\n        self.wait_for(\n            lambda: self.is_element_present(locator) == present,\n            lambda: failure_message,\n            timeout_seconds=timeout_seconds\n        )\n\n\n    def wait_for_element_to_appear(self, locator, timeout_seconds=DEFAULT_WAIT_FOR_TIMEOUT):\n        self.wait_for_element_presence(locator, True, timeout_seconds)\n\n\n    def wait_for_element_text(self, locator, text, timeout_seconds=DEFAULT_WAIT_FOR_TIMEOUT):\n        self.wait_for(\n            lambda: self.get_text(locator) == text,\n            lambda: \"Element %s to contain text %r. Contained %r\" % (locator, text, self.get_text(locator)),\n            timeout_seconds=timeout_seconds\n        )\n\n\n    def wait_for_element_visibility(self, locator, visibility, timeout_seconds=DEFAULT_WAIT_FOR_TIMEOUT):\n        self.wait_for(\n            lambda : self.selenium.is_visible(locator) == visibility,\n            lambda : \"Element %s to become%svisible\" % (locator, visibility and ' ' or ' in'),\n            timeout_seconds=timeout_seconds\n        )\n\n\n    def get_url_with_session_cookie(self, url, data=None):\n        opener = urllib2.build_opener()\n        session_cookie = self.selenium.get_cookie_by_name('sessionid')\n        opener.addheaders.append(('Cookie', 'sessionid=%s' % (session_cookie, )))\n        if data is None:\n            return opener.open(url)\n        else:\n            encoded_data = urllib.urlencode(data)\n            return opener.open(url, encoded_data)\n\n\n    def create_new_sheet(self, username=None, manually=False):\n        if username is None:\n            username = self.get_my_username()\n        user = User.objects.get(username=username)\n        sheet = Sheet(owner=user)\n        sheet.save()\n        self.browser.get(Url.sheet_page(username, sheet.id))\n        return sheet.id\n\n\n\n    def login_and_create_new_sheet(self, username=None):\n        self.login(username=username)\n        return self.create_new_sheet(username=username)\n\n\n    def get_my_usernames(self):\n        usernames = []\n        for user_index in range(self.user_count):\n            capture_test_details = re.compile(r'test_(\\d+)_[^\\.]*\\.[^\\.]*\\.test_(.*)$')\n            match = re.search(capture_test_details, self.id())\n            test_task_id = match.group(1)\n            test_method_name = match.group(2)\n            test_method_hash = hashlib.md5(test_method_name).hexdigest()[:7]\n\n            usernames.append((\"tstusr_%s_%s\" % (test_task_id, test_method_hash))[:29] + str(user_index))\n        return usernames\n\n\n    def get_my_username(self):\n        return self.get_my_usernames()[0]\n\n\n    def _check_page_link_home(self):\n        if self.browser.current_url.startswith(Url.DOCUMENTATION):\n            return\n\n        link = None\n        for possible_id in ('id_small_header_logo', 'id_big_logo'):\n            try:\n                link = self.browser.find_element_by_xpath(\n                    \"//a[img[@id='{img_id}']]\".format(img_id=possible_id)\n                )\n                self.assertEqual(link.get_attribute('href'), Url.ROOT)\n                return\n            except NoSuchElementException:\n                pass\n\n        self.fail(\"Could not find a logo that is also a link on page {}\".format(\n            self.browser.current_url\n        ))\n\n\n    def check_page_load(self, link_destination=None):\n        self._check_page_link_home()\n\n\n    def go_to_url(self, url):\n        _debug('going to url ' + url)\n        self.browser.get(url)\n        self.check_page_load(url)\n\n\n    def refresh_sheet_page(self):\n        self.browser.refresh()\n        self.wait_for_grid_to_appear()\n\n\n    def click_link(self, element_id):\n        link = self.browser.find_element_by_id(element_id)\n        link.click()\n\n\n    def set_sheet_name(self, name):\n        self.selenium.click('id=id_sheet_name')\n        self.wait_for(\n            lambda: self.is_element_present('id=edit-id_sheet_name'),\n            lambda: 'editable sheetname to appear')\n        self.selenium.type('id=edit-id_sheet_name', name)\n        self.human_key_press('\\n')\n        self.wait_for(\n            lambda: self.get_text('id=id_sheet_name') == name,\n            lambda: 'sheet name to be updated'\n        )\n\n\n    def assert_sends_to_login_page(self, requested_url):\n        self.assert_redirects(requested_url, Url.LOGIN)\n\n\n    def assert_sends_to_root_page(self, requested_url):\n        self.assert_redirects(requested_url, Url.ROOT)\n\n\n    def assert_page_title_contains(self, link_url, title):\n        original_page = self.browser.current_url\n        self.go_to_url(link_url)\n        self.assertTrue(title in self.browser.title)\n        self.go_to_url(original_page)\n\n\n    def assert_has_useful_information_links(self):\n        self.browser.find_elements_by_link_text('Terms & Conditions')\n        self.browser.find_elements_by_link_text('Privacy Policy')\n        self.browser.find_elements_by_link_text('Contact Us')\n\n\n    def get_cell_css(self, column, row, must_be_active=False):\n        active_classes = ''\n        if must_be_active:\n            active_classes = '.active'\n        return 'div.slick-row[row=\"%d\"] div.slick-cell.c%d%s' % (\n            row - 1, column, active_classes\n        )\n\n\n    def get_cell_locator(self, column, row, must_be_active=False):\n        return 'css=%s' % (self.get_cell_css(column, row, must_be_active),)\n\n\n    def get_cell_formatted_value_locator(self, column, row, raise_if_cell_missing=True):\n        cell_css = self.get_cell_css(column, row)\n        if not self.is_element_present('css=%s' % (cell_css,)):\n            if raise_if_cell_missing:\n                raise Exception(\"Cell not present at %s, %s\" % (column, row))\n            else:\n                return None\n        return 'css=%s span.grid_formatted_value' % (cell_css,)\n\n\n\n    cell_editor_css = 'input.editor-text'\n\n    def get_active_cell_editor_locator(self):\n        return 'css={}'.format(self.cell_editor_css)\n\n\n    def get_cell_editor_locator(self, column, row):\n        cell_css = self.get_cell_css(column, row)\n        return 'css=%s %s' % (cell_css, self.cell_editor_css)\n\n\n    def get_cell_editor(self):\n        return self.get_element(self.get_active_cell_editor_locator())\n\n\n    def is_cell_visible(self, column, row):\n        tries = 0\n        while tries < 4:\n            try:\n                return 'true' == self.selenium.get_eval(dedent(\n                    '''\n                        (function () {\n                            var viewport = window.grid.getViewport();\n                            if (viewport.top > %(row)s || %(row)s > viewport.bottom) {\n                                return false;\n                            }\n\n                            var $canvasDiv = window.$('div.grid-canvas');\n                            var $viewportDiv = window.$('div.slick-viewport');\n                            var viewableLeft = -$canvasDiv.position().left;\n                            var viewableRight = viewableLeft + $viewportDiv.width();\n                            var $currentCell = window.$('%(current_cell_css)s');\n                            var currentCellLeft = $currentCell.position().left;\n                            var currentCellRight = currentCellLeft + $currentCell.outerWidth();\n                            if (viewableLeft > currentCellLeft || currentCellRight > viewableRight) {\n                                return false;\n                            }\n\n                            return true;\n                        })()\n                    ''' % dict(row=row, col=column, current_cell_css=self.get_cell_css(column, row) )\n                ) )\n            except:\n                time.sleep(1)\n                tries += 1\n\n        self.fail(\"Could not check for cell visibility at %s, %s after %s tries\" % (column, row, tries))\n\n\n    def assert_cell_visible(self, column, row):\n        self.assertTrue(\n            self.is_cell_visible(column, row),\n            'cell %s, %s not visible' % (column, row)\n        )\n\n\n    def wait_for_cell_to_be_visible(\n        self, column, row, timeout_seconds=DEFAULT_WAIT_FOR_TIMEOUT\n    ):\n        self.wait_for(\n            lambda: self.is_cell_visible(column, row),\n            lambda: \"Cell at %s, %s to become visible\" % (column, row),\n            allow_exceptions=True,\n            timeout_seconds=timeout_seconds\n        )\n\n\n    def get_formula_bar_id(self):\n        return \"id_formula_bar\"\n\n\n    def get_formula_bar_locator(self):\n        return \"id=%s\" % (self.get_formula_bar_id(),)\n\n\n    def is_formula_bar_enabled(self):\n        return self.is_element_enabled(self.get_formula_bar_id())\n\n\n    def scroll_cell_row_into_view(self, column, row):\n        self.browser.execute_script(\n            'window.grid.scrollRowIntoView({row}, true);'.format(row=row - 1)\n        )\n        self.wait_for_element_to_appear(self.get_cell_locator(column, row))\n\n\n    def go_to_cell(self, column, row):\n        self.selenium.get_eval(\n            'window.grid.gotoCell(%s, %s, false)' % (row - 1, column))\n        self.wait_for_element_to_appear(self.get_cell_locator(column, row))\n\n\n    @humanise_with_delay\n    def click_on_cell(self, column, row):\n        self.scroll_cell_row_into_view(column, row)\n        self.get_element(self.get_cell_locator(column, row)).click()\n\n\n    def select_range_with_shift_click(self, start, end):\n        self.click_on_cell(*start)\n        with self.key_down(key_codes.SHIFT):\n            self.click_on_cell(*end)\n        self.assert_current_selection(start, end)\n\n\n    def mouse_drag(self, cell_from, cell_to):\n        from_locator = self.get_cell_locator(*cell_from)\n        to_locator = self.get_cell_locator(*cell_to)\n\n        pixel_offset = \"10,30\"\n        #pixel offset fixes selenium weird tendency to click too far north-west.\n        #may cause problems if column widths are reduced...\n\n        self.selenium.mouse_down_at(from_locator, pixel_offset)\n        humanesque_delay(1)\n        self.selenium.mouse_move_at(from_locator, pixel_offset)\n        humanesque_delay(1)\n        self.selenium.mouse_move_at(to_locator, pixel_offset)\n        humanesque_delay(1)\n        self.selenium.mouse_up_at(to_locator, pixel_offset)\n        humanesque_delay(1)\n\n\n    def assert_current_selection(self, topleft, bottomright, thoroughly=True):\n        if thoroughly:\n            for row in range(topleft[1],bottomright[1] + 1):\n                for col in range(topleft[0],bottomright[0] + 1):\n                    locator = self.get_cell_locator(col, row) + '.selected'\n                    self.wait_for_element_to_appear(locator)\n        else:\n            topleft_locator = self.get_cell_locator(*topleft) + '.selected'\n            bottomright_locator = (\n                self.get_cell_locator(*bottomright) + '.selected'\n            )\n            self.wait_for_element_to_appear(topleft_locator)\n            self.wait_for_element_to_appear(bottomright_locator)\n\n\n    def open_cell_for_editing(self, column, row):\n        self.scroll_cell_row_into_view(column, row)\n        ActionChains(self.browser).double_click(\n            self.get_element(self.get_cell_locator(column, row))\n        ).perform()\n        self.wait_for_cell_to_enter_edit_mode(column, row)\n\n\n    def type_into_cell_editor_unhumanized(self, text):\n        self.get_cell_editor().send_keys(text)\n\n\n    @humanise_with_delay\n    def enter_cell_text(self, col, row, text):\n        self.enter_cell_text_unhumanized(col, row, text)\n\n\n    def enter_cell_text_unhumanized(self, col, row, text):\n        self.open_cell_for_editing(col, row)\n        self.type_into_cell_editor_unhumanized(text)\n        self.type_into_cell_editor_unhumanized('\\n')\n        # self.wait_for_cell_to_contain_formula(text)\n\n\n    def get_current_cell(self):\n        row = int(self.browser.execute_script(\n            'return window.grid.getActiveCell().row;')\n        ) + 1\n        column = int(self.browser.execute_script(\n            'return window.grid.getActiveCell().cell;'\n        ))\n        return column, row\n\n\n    def get_cell_text(self, column, row):\n        self.scroll_cell_row_into_view(column, row)\n        text = self.get_text(self.get_cell_locator(column, row))\n        return text\n\n\n    def get_cell_editor_content(self):\n        return self.get_cell_editor().get_attribute('value')\n\n\n    def get_cell_shown_formula_locator(self, column, row, raise_if_cell_missing=True):\n        cell_css = self.get_cell_css(column, row)\n        if not self.is_element_present('css=%s' % (cell_css,)):\n            if raise_if_cell_missing:\n                raise Exception(\"Cell not present at %s, %s\" % (column, row))\n            else:\n                return None\n        return 'css=%s span.grid_formula' % (cell_css,)\n\n\n    def get_cell_shown_formula(self, column, row, raise_if_cell_missing=True):\n        formula_locator = self.get_cell_shown_formula_locator(\n            column, row, raise_if_cell_missing\n        )\n        if not self.is_element_present(formula_locator):\n            return None\n\n        formula = self.get_text(formula_locator)\n        return formula\n\n\n    def assert_cell_shown_formula(self, column, row, formula):\n        self.assertEquals(self.get_cell_shown_formula(column, row), formula)\n\n\n    def wait_for_cell_shown_formula(self, column, row, formula, timeout_seconds=DEFAULT_WAIT_FOR_TIMEOUT):\n        def generate_failure_message():\n            return (\n                \"cell %d, %d to show formula '%s', was %r -- text is %r\" % (\n                column, row, formula, self.get_cell_shown_formula(column, row), self.get_cell_text(column, row))\n            )\n\n        self.wait_for(\n            lambda : self.get_cell_shown_formula(column, row, raise_if_cell_missing=False) == formula,\n            generate_failure_message,\n            allow_exceptions=True,\n            timeout_seconds=timeout_seconds\n        )\n\n\n    def wait_for_cell_to_contain_formula(self, column, row, formula):\n        self.open_cell_for_editing(column, row)\n        self.wait_for_cell_editor_content(formula)\n        self.get_cell_editor().send_keys('\\n')\n\n\n    error_img_locator = 'id=id_{col}_{row}_error'\n\n    def get_cell_error(self, column, row):\n        if self.is_element_present(self.error_img_locator.format(col=column, row=row)):\n            return self.get_element(self.error_img_locator.format(col=column, row=row)).get_attribute('title')\n\n\n    def assert_cell_has_error(self, column, row, error_text):\n        self.wait_for_element_to_appear(self.error_img_locator.format(col=column, row=row))\n        self.assertEquals(self.get_cell_error(column, row), error_text)\n\n\n    def assert_cell_has_no_error(self, column, row):\n        self.assertFalse(\n            self.is_element_present(self.error_img_locator.format(col=column, row=row)),\n            'Error present for (%d, %d)' % (column, row)\n        )\n\n\n    def assert_cell_is_current_but_not_editing(self, col, row):\n        self.wait_for_cell_to_become_active(col, row)\n        self.assertFalse(\n            self.is_element_focused(self.get_cell_editor_locator(col, row))\n        )\n\n\n    def assert_cell_is_current_and_is_editing(self, col, row):\n        self.wait_for_cell_to_become_active(col, row)\n        self.assertTrue(\n            self.is_element_focused(self.get_cell_editor_locator(col, row))\n        )\n\n\n    def wait_for_cell_value(\n        self, column, row, value_or_regex,\n        timeout_seconds=DEFAULT_WAIT_FOR_TIMEOUT\n    ):\n        _debug('waiting for cell {},{} value {}'.format(\n            column, row, value_or_regex,\n        ))\n\n        def match(text):\n            if hasattr(value_or_regex, 'match'):\n                return value_or_regex.match(text)\n            else:\n                return text == value_or_regex\n\n        def cell_shows_value():\n            self.last_found_value = self.get_cell_text(column, row)\n            return (\n                match(self.last_found_value) and\n                self.get_cell_shown_formula(\n                    column, row, raise_if_cell_missing=False\n                ) is None\n            )\n\n        def generate_failure_message():\n            actual_value = self.last_found_value\n            self.last_found_value = None\n            actual_formula = ''\n            if self.get_cell_shown_formula(column, row) is not None:\n                actual_formula = self.last_found_value\n                actual_value = ''\n            actual_error = self.get_cell_error(column, row)\n\n            return (\n                \"Cell at (%s, %s) to become %r \"\n                \"(value=%r, shown formula=%r, error=%r)\" % (\n                    column, row, value_or_regex,\n                    actual_value, actual_formula, actual_error)\n            )\n\n        self.last_found_value = None\n        try:\n            self.wait_for(\n                cell_shows_value,\n                generate_failure_message,\n                timeout_seconds=timeout_seconds,\n                allow_exceptions=True\n            )\n        finally:\n            _debug('finished waiting for cell value')\n\n\n    def wait_for_cell_to_become_active(\n        self, column, row, timeout_seconds=DEFAULT_WAIT_FOR_TIMEOUT\n    ):\n        locator = self.get_cell_locator(column, row, must_be_active=True)\n        self.wait_for(\n            lambda: self.is_element_present(locator),\n            lambda: \"Cell at (%s, %s) was not active. Selection is: %s\" % (\n                column, row, self.get_current_cell()\n            ),\n            timeout_seconds=timeout_seconds\n        )\n\n\n    def wait_for_cell_to_enter_edit_mode(\n        self, column, row, timeout_seconds=DEFAULT_WAIT_FOR_TIMEOUT\n    ):\n        self.wait_for_cell_to_become_active(column, row)\n        full_editor_locator = self.get_cell_editor_locator(column, row)\n        self.wait_for(\n            lambda: self.is_element_focused(full_editor_locator),\n            lambda: \"Editor at (%s, %s) to get focus\" % (column, row),\n            timeout_seconds=timeout_seconds\n        )\n\n\n    def wait_for_cell_editor_content(self, content):\n        self.wait_for(\n            lambda: self.get_cell_editor_content() == content,\n            lambda: \"Cell editor to become %s (was '%s')\" % (\n                content, self.get_cell_editor_content()\n            ),\n        )\n\n\n    def get_viewport_top(self):\n        return int(self.selenium.get_eval(\n            'window.grid.getViewport().top'\n        ) ) + 1\n\n\n    def get_viewport_bottom(self):\n        return int(self.selenium.get_eval(\n            'window.grid.getViewport().bottom'\n        ) ) + 1\n\n\n    def is_spinner_visible(self):\n        return (\n            self.is_element_present('css=#id_spinner_image')\n            and not self.is_element_present('css=#id_spinner_image.hidden')\n        )\n\n\n    def wait_for_spinner_to_stop(self, timeout_seconds=DEFAULT_WAIT_FOR_TIMEOUT):\n        self.wait_for(\n            lambda : not self.is_spinner_visible(),\n            lambda : \"Spinner to disappear\",\n            timeout_seconds=timeout_seconds\n        )\n\n\n    def wait_for_grid_to_appear(self, timeout_seconds=DEFAULT_WAIT_FOR_TIMEOUT):\n        self.wait_for_element_to_appear(self.get_cell_locator(1, 1), timeout_seconds)\n\n\n    def get_usercode(self):\n        return self.browser.execute_script(\n            'return window.editor.session.getValue();'\n        ).replace('\\r\\n', '\\n')\n\n\n    @humanise_with_delay\n    def enter_usercode(self, code, commit_change=True):\n        self.browser.execute_script(\n            \"window.editor.session.setValue(%s);\" % (\n                repr(unicode(code))[1:],\n            )\n        )\n        if commit_change:\n            self.human_key_press(Keys.F9)\n\n\n    def append_usercode(self, code):\n        self.enter_usercode(\"%s\\n%s\" % (self.get_usercode(), code))\n\n\n    def prepend_usercode(self, code):\n        self.enter_usercode(\"%s\\n%s\" % (code, self.get_usercode()))\n\n\n    def wait_for_usercode_editor_content(\n        self, content, timeout_seconds=DEFAULT_WAIT_FOR_TIMEOUT\n    ):\n        self.wait_for(\n            lambda: self.get_usercode().strip() == content.strip(),\n            lambda: (\n                'Usercode editor content to become \\n'\n                + content\n                + '\\n' + '-=' * 10 + '\\nwas:\\n'\n                + self.get_usercode()\n            ),\n            timeout_seconds=timeout_seconds\n        )\n\n\n    def sanitise_console_content(self, content):\n        # IE has char 13 for return instead of the normal Unix 10.\n        # Not sure why it differs from Chrome and Firefox.\n        return content.replace('\\r', '\\n')\n\n\n    def get_console_content(self):\n        content = self.selenium.get_eval('window.$(\"#id_console\").text()')\n        return self.sanitise_console_content(content)\n\n\n    def wait_for_console_content(self, content, timeout_seconds=DEFAULT_WAIT_FOR_TIMEOUT):\n        self.wait_for(\n            lambda: content in self.get_console_content(),\n            lambda : 'error console to contain \"%s\" (was \"%s\")' % (content, self.get_console_content()),\n           timeout_seconds=timeout_seconds\n        )\n\n\n    def get_formula_bar_contents(self):\n        return self.selenium.get_value(self.get_formula_bar_locator())\n\n\n    def assert_formula_bar_contains(self, contents):\n        self.assertEquals(self.get_formula_bar_contents(), contents)\n\n\n    def wait_for_formula_bar_contents(self, contents, timeout_seconds=DEFAULT_WAIT_FOR_TIMEOUT):\n        self.wait_for(\n            lambda : self.get_formula_bar_contents() == contents,\n            lambda : 'formula bar to contain \"%s\" (was \"%s\")' % (contents, self.get_formula_bar_contents() ),\n            timeout_seconds=timeout_seconds\n        )\n\n    def click_formula_bar(self):\n        self.selenium.click(self.get_formula_bar_locator())\n        self.wait_for(\n            lambda : self.is_element_focused(self.get_formula_bar_locator()),\n            lambda : \"Formula bar to gain focus\"\n        )\n\n\n    def copy_range(self, start, end):\n        self.click_on_cell(*start)\n        with self.key_down(key_codes.SHIFT):\n            self.click_on_cell(*end)\n        self.assert_current_selection(start, end)\n        with self.key_down(key_codes.CTRL):\n            self.selenium.key_press_native(key_codes.LETTER_C)\n\n\n    def cut_range(self, start, end):\n        self.click_on_cell(*start)\n        with self.key_down(key_codes.SHIFT):\n            self.click_on_cell(*end)\n        self.assert_current_selection(start, end)\n        with self.key_down(key_codes.CTRL):\n            self.selenium.key_press_native(key_codes.LETTER_X)\n        self.wait_for_spinner_to_stop()\n\n\n    def paste_range(self, start, end=None):\n        self.click_on_cell(*start)\n        if end:\n            with self.key_down(key_codes.SHIFT):\n                self.click_on_cell(*end)\n        with self.key_down(key_codes.CTRL):\n            self.selenium.key_press_native(key_codes.LETTER_V)\n        self.wait_for_spinner_to_stop()\n\n\n    def set_filename_for_upload(self, file_name, field_selector):\n        if self.selenium.browserStartCommand == '*firefox':\n            self.selenium.focus(field_selector)\n            self.selenium.type(field_selector, file_name)\n            self.selenium.click(field_selector)\n        else:\n            def handle_file_dialog():\n                time.sleep(2)\n                SendKeys.SendKeys('{ENTER}')\n                time.sleep(2)\n                escaped_filename = file_name.replace('~','{~}')\n                SendKeys.SendKeys(escaped_filename)\n                SendKeys.SendKeys('{ENTER}')\n                time.sleep(2)\n\n            dialog_thread = Thread(target=handle_file_dialog)\n            dialog_thread.start()\n            self.selenium.click(field_selector)\n            self.selenium.focus(field_selector)\n            dialog_thread.join()\n\n\n    def pop_email_for_client(self, email_address, fail_if_none=True, content_filter=None):\n        retries = 6\n        while retries:\n            message = self._pop_email_for_client_once(email_address, content_filter=content_filter)\n            if message:\n                return message\n            else:\n                retries -= 1\n                if retries == 0:\n                    if fail_if_none:\n                        self.fail('Email not received')\n                time.sleep(5)\n\n\n    def _pop_email_for_client_once(self, email_address, content_filter=None):\n        from imapclient import IMAPClient\n        message = None\n        messages_to_delete = []\n        server = IMAPClient(IMAP_HOST, ssl=True)\n        for m_id, parsed_headers, body_text in self.all_emails(server):\n            if email_address in parsed_headers['To']:\n                body_text = body_text.replace('\\r', '')\n                body_text = body_text.replace('=\\n', '')\n                if content_filter is None or content_filter in body_text:\n                    message = (\n                        parsed_headers['From'],\n                        parsed_headers['To'],\n                        parsed_headers['Subject'],\n                        body_text\n                    )\n                    messages_to_delete.append(m_id)\n        server.delete_messages(messages_to_delete)\n        return message\n\n\n    def clear_email_for_address(self, email_address, content_filter=None):\n        from imapclient import IMAPClient\n        server = IMAPClient(IMAP_HOST, ssl=True)\n        messages_to_delete = []\n        for m_id, parsed_headers, body_text in self.all_emails(server):\n            if email_address in parsed_headers['To']:\n                if content_filter is None or content_filter in body_text:\n                    messages_to_delete.append(m_id)\n        server.delete_messages(messages_to_delete)\n\n\n    def all_emails(self, server):\n        server.login(IMAP_USERNAME, IMAP_PASSWORD)\n        server.select_folder('INBOX')\n        messages = server.search(['NOT DELETED'])\n        response = server.fetch(messages, ['RFC822.TEXT', 'RFC822.HEADER'])\n        parser = Parser()\n        for m_id, m in response.items():\n            parsed_headers = parser.parsestr(m['RFC822.HEADER'])\n            body_text = m['RFC822.TEXT']\n            yield (m_id, parsed_headers, body_text)\n\n"
  },
  {
    "path": "dirigible/fts/tests/test_2521_CodeEditor.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom selenium.webdriver.common.keys import Keys\nfrom textwrap import dedent\n\nfrom functionaltest import FunctionalTest, snapshot_on_error\n\n\nclass Test_2521_CodeEditor(FunctionalTest):\n\n    def test_code_editor_tabs_and_indents(self):\n        # * Harold wants an editor with syntax coloring and other good stuff\n        #   instead of a boring textarea\n\n        # * He logs in and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He notes that the code editor is an Ace editor (!)\n        self.assertTrue(\n            self.is_element_present('css=#id_usercode.ace_editor'),\n            'editor component not present'\n        )\n\n        # * He plays around with the code editor and discovers that\n        # tabs are converted to 4 spaces, and it autoindents\n\n        original_code = self.get_usercode()\n        self.get_element('id=id_usercode').click()\n        self.human_key_press('a')\n        self.human_key_press('\\n')\n        self.human_key_press(Keys.TAB)\n        self.human_key_press('b')\n        self.human_key_press('\\n')\n        self.human_key_press('c')\n        self.human_key_press('\\n')\n\n        four_spaces = '    '\n        autoindent = four_spaces\n        expected_code_after_typing = (\n            '{original_code}a\\n'\n            '{four_spaces}b\\n'\n            '{autoindent}c\\n'\n        ).format(\n            original_code=original_code,\n            four_spaces=four_spaces,\n            autoindent=autoindent\n        )\n        self.wait_for_usercode_editor_content(expected_code_after_typing)\n\n        # ... undo works\n        with self.key_down(Keys.CONTROL):\n            self.human_key_press('z')\n\n        with self.key_down(Keys.CONTROL):\n            self.human_key_press('z')\n\n        expected_code_after_undo = (\n            '{original_code}a\\n'\n            '{four_spaces}b\\n'\n        ).format(\n            original_code=original_code,\n            four_spaces=four_spaces,\n        )\n        self.wait_for_usercode_editor_content(expected_code_after_undo)\n\n        # ... and redo works\n        with self.key_down(Keys.CONTROL):\n            self.human_key_press('y')\n        with self.key_down(Keys.CONTROL):\n            self.human_key_press('y')\n        self.wait_for_usercode_editor_content(expected_code_after_typing)\n\n        # and typing normally again is fine\n        self.human_key_press('abcabc')\n        self.wait_for(\n            lambda: 'abcabc' in self.get_usercode(),\n            lambda: 'could not find abcabc in {}'.format(self.get_usercode()),\n        )\n\n\n\n    @snapshot_on_error\n    def test_code_editor_shows_errors(self):\n        # * Harold makes mistakes when writing Python and wants Dirigible to\n        #   tell him about them so he can fix them\n\n        error_locator = 'css=div.ace_gutter-cell.ace_error'\n\n        def assert_error(line, message):\n            self.wait_for_element_to_appear(error_locator)\n            error = self.get_element(error_locator)\n            self.assertEquals(error.text, str(line))\n            self.assertEquals(error.get_attribute('title'), message)\n\n\n        # * He logs in and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He enters a value into the grid\n        self.enter_cell_text(1, 1, 'herrrroooo')\n\n        original_usercode = self.get_usercode()\n\n        # * He enters some code that creates a syntax error\n        self.prepend_usercode('import sys:')\n\n        # ... and notes that an error appears in the code editor, and the value\n        # in the grid is grey because the load_constants in the usercode was\n        # never executed.\n        self.wait_for_cell_shown_formula(1, 1, 'herrrroooo')\n\n        assert_error(1, 'Syntax error at character 11')\n\n        # * He refreshes the page, just because he's ornery and notes that the\n        # grid is intact and the error is marked\n\n        self.refresh_sheet_page()\n\n        self.assert_cell_shown_formula(1, 1, 'herrrroooo')\n\n        assert_error(1, 'Syntax error at character 11')\n\n        # * He deletes his mistake and, the error indicator goes away and the\n        #   text ungreys\n        self.enter_usercode(original_usercode)\n        self.wait_for_cell_value(1, 1, 'herrrroooo')\n        self.wait_for_element_presence(error_locator, False)\n\n        # * Not satisfied, he tries a different type of error, this one after\n        # the usercode loads the constants.\n        original_usercode_lines = len(original_usercode.split('\\n'))\n        self.append_usercode('x = newvalue')\n\n        # Once again, he notes that his grid survives, an error appears in the\n        # right place.  However, this time the cell value is not grey.\n        self.wait_for_cell_value(1, 1, 'herrrroooo')\n        assert_error(\n            original_usercode_lines + 1,\n            u\"NameError: name \\u2019newvalue\\u2019 is not defined\"\n        )\n\n\n    def get_editor_selected_range(self):\n        returned_dict = self.browser.execute_script(dedent(\n            \"\"\"\n            var selection = window.editor.getSelectionRange();\n            return (\n                selection.start.column + \", \" + selection.start.row + \", \" +\n                selection.end.column + \", \" + selection.end.row\n            );\n            \"\"\"\n        ))\n        return eval(returned_dict)\n\n\n    def assert_editor_line_visible(self, line):\n        first_visible_row = self.browser.execute_script(\n            \"return window.editor.getFirstVisibleRow()\"\n        )\n        last_visible_row = self.browser.execute_script(\n            \"return window.editor.getLastVisibleRow()\"\n        )\n        self.assertLess(int(first_visible_row), line)\n        self.assertGreater(int(last_visible_row), line)\n\n\n    @snapshot_on_error\n    def test_code_editor_find_function(self):\n        # * Harold logs in and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # He enters some long and complicated usercode, which contains the string \"123\" at a well-known place,\n        # and doesn't hit \"save\".\n        code = \"\"\n        for i in range(100):\n            code += \"a\\n\"\n        code += \"abc123def\"\n        for i in range(100):\n            code += \"a\\n\"\n\n        self.enter_usercode(code, commit_change=False)\n\n        # He hits ^F, and types 123 into the resulting dialog.\n        self.get_element('id=id_usercode').click()\n        with self.key_down(Keys.CONTROL):\n            self.human_key_press('f')\n        alert = self.browser.switch_to_alert()\n        alert.send_keys(123)\n        alert.accept()\n\n        # The editor jumps to the \"123\" bit and selects it.\n        self.assertEquals(self.get_editor_selected_range(), (3, 100, 6, 100))\n\n        # The line is, of course, visible.\n        self.assert_editor_line_visible(100)\n\n\n\n    @snapshot_on_error\n    def test_code_editor_go_to_line_function(self):\n        # * Harold logs in and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # He enters some long and complicated usercode and doesn't hit \"save\".\n        code = \"\"\n        for i in range(200):\n            code += \"a\\n\"\n\n        self.enter_usercode(code, commit_change=False)\n\n        # He hits ^L, and types 100 into the resulting dialog.\n        self.get_element('id=id_usercode').click()\n        with self.key_down(Keys.CONTROL):\n            self.human_key_press('l')\n        alert = self.browser.switch_to_alert()\n        alert.send_keys(100)\n        alert.accept()\n\n        # The editor jumps to line 100\n        self.assertEquals(self.get_editor_selected_range(), (0, 99, 0, 99))\n\n        # The line is, of course, visible.\n        self.assert_editor_line_visible(100)\n"
  },
  {
    "path": "dirigible/fts/tests/test_2525_LoginLogout.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom urlparse import urlparse\n\nfrom functionaltest import FunctionalTest, snapshot_on_error, Url\n\n\nclass Test_2525_LoginLogout(FunctionalTest):\n\n    user_count = 2\n\n    def assert_login_error_shown(self):\n        self.assertEquals(\n            self.get_text('id=id_login_error'),\n            \"The user name or password is incorrect. Please try again.\"\n        )\n\n\n    def test_login_happy_path(self):\n        # Harold logs in\n        self.go_to_url(Url.LOGIN)\n\n        # Finally, he enters the correct username and password.\n        self.get_element('id=id_username').clear()\n        self.get_element('id=id_password').clear()\n        self.get_element('id=id_username').send_keys(self.get_my_username())\n        self.get_element('id=id_password').send_keys('p4ssw0rd')\n        self.click_link('id_login')\n\n        # He is taken to a page entitled \"XXXX's Dashboard: Dirigible\" at the site's root URL.\n        self.assertEquals(\n            self.browser.title,\n            \"{}'s Dashboard: Dirigible\".format(self.get_my_username())\n        )\n        _, __, path, ___, ____, _____ = urlparse(self.browser.current_url)\n        self.assertEquals(path, '/')\n\n        # He sees links to the terms and conditions and suchlike at the bottom of the page.\n        self.assert_has_useful_information_links()\n\n        # On the page is a \"Log Out\" link\n        self.assertEquals(self.get_text('id=id_logout_link'), \"Log out\")\n\n\n    @snapshot_on_error\n    def test_login(self):\n        # Harold goes to a specific URL.\n        self.go_to_url(Url.LOGIN)\n\n        print('title')\n        # He sees a web page with \"Login: Dirigible\" in the title bar.\n        self.assertEquals(self.browser.title, 'Login: Dirigible')\n        welcome_url = self.browser.current_url\n\n        print('focus')\n        # and notices that his cursor is in the username field\n        self.assertTrue(self.is_element_focused('id=id_username'))\n\n        print('userful info')\n        # He sees links to the terms and conditions and suchlike at the bottom of the page.\n        self.assert_has_useful_information_links()\n\n        print('links')\n        # He sees links to the terms and conditions and suchlike at the bottom of the page.\n        # He notices a login link on the page and sees that it leads back to this page\n        login_link = self.get_element('css=a#id_login_link')\n        self.assertEqual(login_link.get_attribute('href'), Url.LOGIN)\n\n        # There is also a \"sign up\" link that would take him to the signup page.\n        signup_link = self.get_element('css=a#id_signup_link')\n        self.assertEqual(signup_link.get_attribute('href'), Url.SIGNUP)\n\n        print('inputs')\n        # The page also contains places where he can enter a user name and a password, and\n        # a login button.\n        self.get_element('css=input#id_username')\n        self.get_element('css=input#id_password')\n        self.get_element('css=input#id_login[type=submit]')\n\n        print('first click')\n        # He enters neither, and clicks the login button.\n        self.click_link('id_login')\n\n        # He is taken back to a copy of the login page where he is additionally chided for\n        # not entering his details.\n        self.assert_login_error_shown()\n\n        # He enters a username but no password\n        self.get_element('id=id_username').send_keys('confused_user')\n        self.click_link('id_login')\n\n        # He is taken back to a copy of the login page where he is told he must enter a\n        # password.  The username is still there.\n        self.assert_login_error_shown()\n        self.assertEquals(self.get_value('id=id_username'), 'confused_user')\n\n        # He enters a password but no username\n        self.get_element('id=id_username').clear()\n        self.get_element('id=id_password').send_keys('confused_pass')\n        self.click_link('id_login')\n\n        # He is taken back to a copy of the login page that patiently reminds him that he\n        # should enter a username.  The password is cleared\n        self.assert_login_error_shown()\n        self.assertEquals(self.get_value('id=id_username'), '')\n        self.assertEquals(self.get_value('id=id_password'), '')\n\n        # He enters the wrong username and password\n        self.get_element('id=id_username').clear()\n        self.get_element('id=id_password').clear()\n        self.get_element('id=id_username').send_keys('wrong user')\n        self.get_element('id=id_password').send_keys('wrong password')\n        self.click_link('id_login')\n\n        # He is taken back to a copy of the login page telling him that the username/password\n        # combo wasn't recognised.  username stays, password goes\n        self.assert_login_error_shown()\n        self.assertEquals(self.get_value('id=id_username'), 'wrong user')\n        self.assertEquals(self.get_value('id=id_password'), '')\n\n        # He corrects the username, but enters the wrong password\n        username = self.get_my_username()\n        self.get_element('id=id_username').clear()\n        self.get_element('id=id_password').clear()\n        self.get_element('id=id_username').send_keys(username)\n        self.get_element('id=id_password').send_keys('wrong password')\n        self.click_link('id_login')\n\n        # He is taken back to a copy of the login page telling him that the username/password\n        # combo wasn't recognised.  username stays, password goes\n        self.assert_login_error_shown()\n        self.assertEquals(self.get_value('id=id_username'), username)\n        self.assertEquals(self.get_value('id=id_password'), '')\n\n        # He enters the correct password, but takes the opportunity to make the username\n        # incorrect.\n        self.get_element('id=id_username').clear()\n        self.get_element('id=id_password').clear()\n        self.get_element('id=id_username').send_keys('wrong user')\n        self.get_element('id=id_password').send_keys('p4ssw0rd')\n        self.click_link('id_login')\n\n        # He is taken back to a copy of the login page telling him that the username/password\n        # combo wasn't recognised.  username stays, password goes\n        self.assert_login_error_shown()\n        self.assertEquals(self.get_value('id=id_username'), 'wrong user')\n        self.assertEquals(self.get_value('id=id_password'), '')\n\n        # Finally, he enters the correct username and password.\n        self.get_element('id=id_username').clear()\n        self.get_element('id=id_password').clear()\n        self.get_element('id=id_username').send_keys(username)\n        self.get_element('id=id_password').send_keys('p4ssw0rd')\n        self.click_link('id_login')\n\n        # He is taken to a page entitled \"XXXX's Dashboard: Dirigible\" at the site's root URL.\n        self.assertEquals(\n            self.browser.title,\n            \"{}'s Dashboard: Dirigible\".format(username)\n        )\n        _, __, path, ___, ____, _____ = urlparse(self.browser.current_url)\n        self.assertEquals(path, '/')\n\n        # He sees links to the terms and conditions and suchlike at the bottom of the page.\n        self.assert_has_useful_information_links()\n\n        # On the page is a \"Log Out\" link\n        self.assertEquals(self.get_text('id=id_logout_link'), \"Log out\")\n\n        # He follows it.\n        self.click_link('id_logout_link')\n\n        # He is taken back to the \"Login: Dirigible\" page he saw at the start of this FT.\n        self.assertEquals(self.browser.current_url, welcome_url)\n        self.assertEquals(self.browser.title, 'Login: Dirigible')\n\n        # He logs in again\n        self.get_element('id=id_username').send_keys(username)\n        self.get_element('id=id_password').send_keys('p4ssw0rd')\n        self.click_link('id_login')\n\n        # He goes back to the login page and is presented with the option to login\n        # and the page also includes 'My account' and 'Logout' links\n        self.go_to_url(Url.LOGIN)\n        self.get_element('css=input#id_username')\n        self.get_element('css=input#id_password')\n        self.assertEquals(self.get_text('id=id_logout_link'), \"Log out\")\n        self.assertEquals(self.get_text('id=id_account_link'), \"My account\")\n\n        # He is satisfied, and calls it a day.\n\n\n    def test_legacy_dashboard_link_takes_you_to_root_url(self):\n        harriet = self.get_my_usernames()[1]\n        harold = self.get_my_username()\n        harolds_dashboard_url = '/user/%s/' % (harold,)\n        harriets_dashboard_url = '/user/%s/' % (harriet,)\n\n        # Before logging in, Harold tries to access his own dashboard using the\n        # old-style URL.\n        # He gets sent to the root page.\n        self.assert_sends_to_root_page(harolds_dashboard_url)\n\n        # Before logging in, Harold tries to access Harriet's dashboard using the\n        # old-style URL.\n        # He gets redirected to the root page\n        self.assert_sends_to_root_page(harriets_dashboard_url)\n\n        # Before logging in, Harold also tries to access a non-existent user's dashboard using the\n        # old-style URL.\n        # He gets sent to the root page.\n        self.assert_sends_to_root_page('/user/non-existent-user/')\n\n        # After logging in, Harold tries to access his own dashboard using the\n        # old-style URL.\n        # He gets sent to the root page.\n        self.login(username=harold)\n        self.assert_sends_to_root_page(harolds_dashboard_url)\n\n        # After logging in, Harold tries to access Harriet's dashboard using the\n        # old-style URL.\n        # He gets sent to the root page.\n        self.assert_sends_to_root_page(harriets_dashboard_url)\n\n        # After logging in, Harold also tries to access a non-existent user's dashboard using the\n        # old-style URL.\n        # He gets sent to the root page.\n        self.assert_sends_to_root_page('/user/non-existent-user/')\n"
  },
  {
    "path": "dirigible/fts/tests/test_2528_CreateEditSheet.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom selenium.webdriver.common.keys import Keys\nfrom urlparse import urlparse\n\nfrom functionaltest import FunctionalTest, snapshot_on_error, Url\n\n\nclass Test_2528_CreateEditSheet(FunctionalTest):\n    user_count = 2\n\n    def assert_editing_cell(self, column, row):\n        self.assertTrue(self.is_element_present(\n            self.get_cell_editor_locator(column, row)\n        ))\n\n\n    @snapshot_on_error\n    def test_create_edit_sheet(self):\n        # * Harold logs in to Dirigible.\n        self.login()\n\n        # * On his dashboard, he notes an option to create a new spreadsheet.\n        # He chooses it.\n        self.assertEquals(self.get_text('id=id_create_new_sheet'), \"Create new sheet...\")\n        self.click_link('id_create_new_sheet')\n\n        # * He is taken to a web page which has a URL like /user/XXXX/sheet/<num>\n        #        where XXXX is his user name\n        _, __, path, ___, ____, _____ = urlparse(self.browser.current_url)\n        self.assertRegexpMatches(path, '/user/%s/sheet/[0-9]+/' % (self.get_my_username(),))\n\n        # * The page has a grid.\n        print('check grid')\n        self.assertTrue(self.is_element_present('id=id_grid'))\n\n        # * He sees that the grid is a usable size (at least 100x100)\n        print('check grid 2')\n        self.wait_for_grid_to_appear()\n        print('check width')\n        self.assertTrue(self.get_element('id=id_grid').size['width'] >= 100)\n        self.assertTrue(self.get_element('id=id_grid').size['height'] >= 100)\n\n        # * Now that the grid is loaded (and so the sheet's name is too), he notices\n        #   that the title is something like: XXXX's sheet_name: Dirigible\n        sheet_name = self.get_text('id=id_sheet_name')\n        self.assertEquals(\n            self.browser.title,\n            \"%s's %s: Dirigible\" % (self.get_my_username(), sheet_name)\n        )\n\n\n        # * He sees that the grid is reasonably layed out, and it has a sensible\n        #   number of rows and columns (at least 10x10), the columns having\n        #   headers A, B, C etc and the rows 1, 2, 3,...\n        column_list_css = 'div.slick-header-column span.slick-column-name'\n\n        def get_columns():\n            return self.browser.find_elements_by_css_selector(column_list_css)\n\n        ## Check for 10 cols plus one header col\n        print('check num cols')\n        self.wait_for(\n            lambda: len(get_columns()) >= 11,\n            lambda: 'column count to become >= 11, was {}'.format(len(get_columns())),\n        )\n\n        print('col positions')\n        col_header_vertical_positions = []\n        for letter in 'ABCDEFG':\n            column = self.get_element(\"css=div.slick-header-column[title={}]\".format(letter))\n            col_header_vertical_positions.append(column.location['y'])\n        self.assertEquals(len(set(col_header_vertical_positions)), 1)\n\n        print('col positions horiz')\n        col_header_horizontal_positions = []\n        for letter in 'ABCDEFG':\n            column = self.get_element(\"css=div.slick-header-column[title={}]\".format(letter))\n            col_header_horizontal_positions.append(column.location['x'])\n        self.assertEquals(\n            col_header_horizontal_positions, sorted(col_header_horizontal_positions)\n        )\n\n        print('col headers')\n        column_headers = [c.text for c in get_columns()]\n        print(column_headers)\n        self.assertEquals(''.join(column_headers[:8]), \"ABCDEFG\")  # starts with one blank\n\n        ## Check for 10 rows plus one header row\n        print('rows')\n        row_list_css = 'div.slick-row'\n        rows = self.browser.find_elements_by_css_selector(row_list_css)\n        ## SlickGrid handles the header row for us, so we don't have to check for it\n        self.assertGreater(len(rows), 9)\n        row_header_vertical_positions = []\n        for row in rows:\n            row_header_vertical_positions.append(row.location['y'])\n\n        self.assertEquals(row_header_vertical_positions, sorted(row_header_vertical_positions))\n\n        row_header_horizontal_positions = set()\n        for row in rows:\n            row_header_horizontal_positions.add(row.location['x'])\n        self.assertEquals(len(row_header_horizontal_positions), 1)\n\n        row_headers = ''.join(r.text for r in rows)\n        self.assertEquals(row_headers[:9], \"123456789\")\n\n        # * He enters \"1\" in A1.\n        print('enter text')\n        self.enter_cell_text(1, 1, '1')\n        print('text entered')\n\n        # * When he moves away from the cell, \"1\" is there\n        print('clicking cell')\n        self.click_on_cell(2, 2)\n        print('cell clicked')\n\n        print('wait for value')\n        self.wait_for_cell_value(1, 1, '1')\n        print('value waited')\n\n        # * He enters \"2\" in A2.  Similarly, this persists when he moves away.\n        print('enter text')\n        self.enter_cell_text(1, 2, '2')\n        print('text entered')\n\n        print('clicking cell')\n        self.click_on_cell(2, 3)\n        print('cell clicked')\n\n        print('wait for value')\n        self.wait_for_cell_value(1, 2, '2')\n        print('value waited')\n\n        # * He enters \"=a1+A2\" in A3.  When he moves away, he sees \"3\"\n        self.enter_cell_text(1, 3, '=11+22')\n        self.click_on_cell(2, 4)\n        self.wait_for_cell_value(1, 3, '33')\n\n        # When he edits the cell again, he is presented with the formula as he entered it\n        self.open_cell_for_editing(1, 3)\n        self.wait_for_cell_editor_content('=11+22')\n\n        # He moves the cursor left and right while editing and the edited cell does not move\n        print('checking arrow keys')\n        self.human_key_press(Keys.LEFT)\n        self.human_key_press(Keys.RIGHT)\n        self.assert_editing_cell(1, 3)\n\n        self.open_cell_for_editing(3, 3)\n        self.human_key_press(Keys.LEFT)\n        self.human_key_press(Keys.LEFT)\n        self.assert_editing_cell(3, 3)\n\n        # * He enters \"=a1+A2\" (NB case!) in A4.  When he moves away, he sees \"3\"\n        self.enter_cell_text(1, 4, '=a1+A2')\n        self.click_on_cell(2, 5)\n        self.wait_for_cell_value(1, 4, '3')\n\n        # When he edits the cell again, he is presented with the formula as he entered it\n        self.open_cell_for_editing(1, 4)\n        self.wait_for_cell_editor_content('=a1+A2')\n\n\n    def test_new_sheet_not_logged_in(self):\n        # * Harold goes to /new_sheet without logging in\n        # * He gets redirected to the login page\n        self.assert_sends_to_login_page(Url.NEW_SHEET)\n\n        # * He logs in\n        self.login(manually=True)\n\n        # * ... and gets take to a new sheet\n        url = urlparse(self.browser.current_url)\n        self.assertEquals(url.netloc, urlparse(Url.ROOT).netloc)\n        self.assertRegexpMatches(url.path, '/user/%s/sheet/[0-9]+/' % (self.get_my_username(),))\n\n\n    @snapshot_on_error\n    def test_access_sheet_with_incorrect_user_id(self):\n        ## Create sheet as user 1, for the rest of the test\n        harriet = self.get_my_usernames()[1]\n        harold = self.get_my_username()\n        sheet_id = self.login_and_create_new_sheet(username=harriet)\n        self.logout()\n        harolds_broken_sheet_url = Url.sheet_page(harold, sheet_id)\n\n        # Before logging in, Harold tries to access one of Harriet's sheets\n        # using the wrong direct URL, with his username but the correct sheet ID.\n        # He gets a 404\n        self.assert_HTTP_error(harolds_broken_sheet_url, 404)\n\n        # After logging in, Harold tries to access one of Harriet's sheets\n        # using the wrong direct URL, with his username but the correct sheet ID.\n        # He gets a 404.\n        self.login(username=harold)\n        self.assert_HTTP_error(harolds_broken_sheet_url, 404)\n"
  },
  {
    "path": "dirigible/fts/tests/test_2529_HighlightErrorsInCells.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom functionaltest import FunctionalTest\n\n\nclass Test_2529_HighlightErrorsInCells(FunctionalTest):\n\n    def test_highlight_errors_in_cells(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He enters \"=lambda x:x + 1\" in A1.\n        self.enter_cell_text(2, 3, '=lambda x:x + 1')\n\n        # * and notes that there is an error marked for that cell\n        self.assert_cell_has_error(\n            2, 3,\n            \"FormulaError: Error in formula at position 10: unexpected ':'\"\n        )\n\n        # * He then enters '=my_value' in A2\n        self.enter_cell_text(4, 5, '=my_value')\n\n        # * and notes that A2 complains that my_value is not defined\n        self.assert_cell_has_error(4, 5, \"NameError: name 'my_value' is not defined\")\n\n        # * He fixes his errors and notes that the error markers go away\n        self.enter_cell_text(2, 3, '=lambda x->x + 1')\n        self.enter_cell_text(4, 5, '=10')\n        self.wait_for_cell_value(4, 5, '10')\n\n        self.assert_cell_has_no_error(2, 3)\n        self.assert_cell_has_no_error(4, 5)\n\n\n"
  },
  {
    "path": "dirigible/fts/tests/test_2531_DifficultStuffInCells.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom textwrap import dedent\n\nfrom functionaltest import FunctionalTest, snapshot_on_error, Url\n\n\nclass Test_2531_DifficultStuffInCells(FunctionalTest):\n\n    @snapshot_on_error\n    def test_list_in_cell(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He enters \"=[1, 2, 3]\" in A1.\n        self.enter_cell_text(1, 1, '=[1, 2, 3]')\n\n        # * When he moves away from the cell, \"[1, 2, 3]\" is there\n        self.click_on_cell(2, 1)\n        self.wait_for_cell_value(1, 1, '[1, 2, 3]')\n\n        # * He enters a list comprehension in A2.\n        self.enter_cell_text(1, 2, '=[x*2 for x in A1]')\n        self.click_on_cell(2, 2)\n        self.wait_for_cell_value(1, 2, '[2, 4, 6]')\n\n\n    def test_dictionary_in_cell(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He enters \"={'key'->'value', 'key2'->3}\" in A1.\n        self.enter_cell_text(1, 1, \"={'key'  ->'value', 'key2'->  3}\")\n\n        # * When he moves away from the cell, \"{'key':'value', 'key2':3}\" is there\n        self.click_on_cell(2, 1)\n        self.wait_for_cell_value(1, 1, repr({'key':'value', 'key2':3}))\n\n\n    def test_object_in_cell(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He enters \"=object()\" in A1.\n        self.enter_cell_text(1, 1, '=object()')\n        self.click_on_cell(2, 1)\n\n        # * When he moves away from the cell, the obj is visible\n        self.wait_for(\n            lambda: self.get_cell_text(1, 1).startswith('<object object at 0x'),\n            lambda: 'object to appear in cell')\n\n        # * Feeling perverse, he decides to create his own class and shove it into the grid\n        self.prepend_usercode(dedent('''\n        class Perverse(object):\n            t = 'blah'\n\n        p = Perverse()\n        '''))\n        self.enter_cell_text(3, 1, '=p')\n        self.click_on_cell(3, 4)\n\n        # * He sees the string representation of his new object\n        self.wait_for(\n            lambda: self.get_cell_text(3, 1).startswith('<Perverse object at 0x'),\n            lambda: 'user-defined object to appear in cell')\n\n\n    def assert_input_roundtrips(self, typed):\n        # * He enters an html tag in A1\n        self.row += 1\n        self.enter_cell_text(1, self.row, typed)\n        self.wait_for_cell_value(1, self.row, typed)\n        self.open_cell_for_editing(1, self.row)\n        self.wait_for_cell_editor_content(typed)\n\n        self.enter_cell_text(2, self.row, '=\"%s\"' % (typed, ))\n        self.wait_for_cell_value(2, self.row, '%s' % (typed,))\n        self.open_cell_for_editing(2, self.row)\n        self.wait_for_cell_editor_content('=\"%s\"' % (typed, ))\n\n\n    def test_html_chars_escaped(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He enters html tags and chars that look like escaped html tags\n        self.row = 1\n        self.assert_input_roundtrips('<br />')\n        self.assert_input_roundtrips('fish&chips')\n        self.assert_input_roundtrips('&lt;')\n        self.assert_input_roundtrips('&gt;')\n        self.assert_input_roundtrips('&amp;')\n\n\n    @snapshot_on_error\n    def test_numeric_types(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        self.enter_cell_text(1, 1, '999')\n        self.wait_for_cell_value(1, 1, '999')\n        self.open_cell_for_editing(1, 1)\n        self.wait_for_cell_editor_content('999')\n        self.append_usercode(\"worksheet[1, 2].value = type(worksheet[1, 1].value)\")\n        self.wait_for_cell_value(1, 2, \"<type 'int'>\")\n\n        self.enter_cell_text(2, 1, '999.0')\n        self.wait_for_cell_value(2, 1, '999.0')\n        self.open_cell_for_editing(2, 1)\n        self.wait_for_cell_editor_content('999.0')\n        self.append_usercode(\"worksheet[2, 2].value = type(worksheet[2, 1].value)\")\n        self.wait_for_cell_value(2, 2, \"<type 'float'>\")\n"
  },
  {
    "path": "dirigible/fts/tests/test_2532_LambdasInCells.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nimport re\nimport time\nfrom urlparse import urlparse\nfrom textwrap import dedent\n\nfrom browser_settings import SERVER_IP\n\nfrom functionaltest import FunctionalTest, snapshot_on_error\n\n\nclass Test_2532_LambdasInCells(FunctionalTest):\n\n    @snapshot_on_error\n    def test_lambda_in_cell(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He enters \"=lambda x -> x + 1\" in A1.\n        self.enter_cell_text(1, 1, '=lambda x -> x + 1')\n\n        # * He enters =A1(5) into B1\n        self.enter_cell_text(2, 1, '=A1(5)')\n\n        # * and notes that the value in B1 is 6\n        self.wait_for_cell_value(2, 1, '6')\n\n"
  },
  {
    "path": "dirigible/fts/tests/test_2533_Numpy.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\n\nfrom functionaltest import FunctionalTest, snapshot_on_error\n\n\nclass Test_2533_Numpy(FunctionalTest):\n\n    @snapshot_on_error\n    def test_numpy_tutorial_create_arrays(self):\n        # * Harold wants to do the tutorial at http://www.scipy.org/Tentative_NumPy_Tutorial\n        #   in Dirigible\n        # * He logs in and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * At the start of the user code, he imports numpy\n        self.prepend_usercode('import numpy')\n\n        # * He creates a numoy array in cell A1\n        self.enter_cell_text(1, 1, '=numpy.array([2, 3, 4])')\n\n        # * He sees A1 contains the string array([2, 3, 4])\n        self.wait_for_cell_value(1, 1, '[2 3 4]')\n\n        # * He checks the type of the array in A2\n        self.enter_cell_text(1, 2, '=type(A1)')\n        self.wait_for_cell_value(1, 2, \"<type 'numpy.ndarray'>\")\n\n        # * He creates a 2-dimensional array in B1\n        self.enter_cell_text(2, 1, '=numpy.array([(1.5, 2, 3), (4, 5, 6)])')\n        self.wait_for_cell_value(2, 1, \"[[ 1.5 2. 3. ] [ 4. 5. 6. ]]\")\n\n        # * He has a look at some of the properties of the new array\n        self.enter_cell_text(2, 2, '=B1.ndim')\n        self.wait_for_cell_value(2, 2, \"2\")\n        self.enter_cell_text(2, 3, '=B1.shape')\n        self.wait_for_cell_value(2, 3, \"(2, 3)\")\n        self.enter_cell_text(2, 4, '=B1.dtype')\n        self.wait_for_cell_value(2, 4, \"float64\")\n        self.enter_cell_text(2, 5, '=B1.itemsize')\n        self.wait_for_cell_value(2, 5, \"8\")\n\n        # * He creates an array of complex numbers\n        self.enter_cell_text(3, 1, '=numpy.array([[1, 2], [3, 4]], dtype=numpy.complex)')\n        self.wait_for_cell_value(3, 1, \"[[ 1.+0.j 2.+0.j] [ 3.+0.j 4.+0.j]]\")\n\n        # * He creates arrays of zeros and ones\n        self.enter_cell_text(4, 1, '=numpy.zeros((3, 4))')\n        self.wait_for_cell_value(4, 1, \"[[ 0. 0. 0. 0.] [ 0. 0. 0. 0.] [ 0. 0. 0. 0.]]\")\n        self.enter_cell_text(5, 1, '=numpy.ones((2, 3, 4), dtype=numpy.int16)')\n        self.wait_for_cell_value(5, 1, \"[[[1 1 1 1] [1 1 1 1] [1 1 1 1]] [[1 1 1 1] [1 1 1 1] [1 1 1 1]]]\")\n\n        # * He uses arange to create a list of numbers\n        self.enter_cell_text(6, 1, '=numpy.arange(10, 30, 5)')\n        self.wait_for_cell_value(6, 1, \"[10 15 20 25]\")\n\n        # * He creates a sequence of floating point numbers\n        self.enter_cell_text(7, 1, '=numpy.linspace(0, 2, 9)')\n        self.wait_for_cell_value(7, 1, \"[ 0. 0.25 0.5 0.75 1. 1.25 1.5 1.75 2. ]\")\n\n        # * He uses arange to create a very large array and notes that Dirigible displays it semi-intelligently\n        self.enter_cell_text(8, 1, '=numpy.arange(10000)')\n        self.wait_for_cell_value(8, 1, \"[ 0 1 2 ..., 9997 9998 9999]\")\n\n\n    @snapshot_on_error\n    def test_numpy_tutorial_basic_operations(self):\n        # * Harold wants to do the tutorial at http://www.scipy.org/Tentative_NumPy_Tutorial\n        #   in Dirigible\n        # * He logs in and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * At the start of the user code, he imports numpy\n        self.prepend_usercode('import numpy')\n\n        # * He deducts one range from another\n        self.enter_cell_text(1, 1, '=numpy.array([20, 30, 40, 50])')\n        self.enter_cell_text(2, 1, '=numpy.arange(4)')\n        self.enter_cell_text(3, 1, '=a1 - B1')\n        self.wait_for_cell_value(3, 1, \"[20 29 38 47]\")\n\n        # * He squares a range\n        self.enter_cell_text(3, 2, '=B1**2')\n        self.wait_for_cell_value(3, 2, \"[0 1 4 9]\")\n\n        # * He takes the sine of a range\n        self.enter_cell_text(3, 3, '=10*numpy.sin(a1)')\n        self.wait_for_cell_value(3, 3, \"[ 9.12945251 -9.88031624 7.4511316 -2.62374854]\")\n\n        # * Having decided that Dirigible supports the basic operations to his satisfaction,\n        #   he skips ahead to check that it works with complex numbers\n        self.enter_cell_text(1, 1, '=numpy.ones(3, dtype=numpy.int32)')\n        self.enter_cell_text(2, 1, '=numpy.linspace(0, numpy.pi, 3)')\n        self.enter_cell_text(2, 2, '=b1.dtype.name')\n        self.wait_for_cell_value(2, 2, \"float64\")\n        self.enter_cell_text(3, 1, '=a1+b1')\n        self.wait_for_cell_value(3, 1, \"[ 1. 2.57079633 4.14159265]\")\n        self.enter_cell_text(3, 2, '=c1.dtype.name')\n        self.wait_for_cell_value(3, 2, \"float64\")\n        self.enter_cell_text(4, 1, '=numpy.exp(c1 * 1j)')\n        self.wait_for_cell_value(4, 1, \"[ 0.54030231+0.84147098j -0.84147098+0.54030231j -0.54030231-0.84147098j]\")\n        self.enter_cell_text(4, 2, '=d1.dtype.name')\n        self.wait_for_cell_value(4, 2, \"complex128\")\n"
  },
  {
    "path": "dirigible/fts/tests/test_2534_JsonWorksheets.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\ntry:\n    import simplejson as json\nexcept ImportError:\n    import json\n\nfrom urllib import urlencode\nfrom urllib2 import urlopen\n\nfrom functionaltest import FunctionalTest, Url\n\n\nclass Test_2534_JsonWorksheets_v0_1(FunctionalTest):\n\n    def enable_json_api_for_sheet(self):\n        self.selenium.click('id=id_security_button')\n        self.wait_for_element_visibility('id=id_security_form', True)\n        self.selenium.click('id=id_security_form_json_enabled_checkbox')\n        self.selenium.type('id=id_security_form_json_api_key', self.get_my_username())\n        self.selenium.click('id=id_security_form_ok_button')\n        self.wait_for_element_visibility('id=id_security_form', False)\n\n\n    def test_simple_json(self):\n        # * Harold wants to use Dirigible to run his spreadsheets using\n        #   a json-based rest API\n\n        # * He logs in to Dirigible and creates a new sheet\n        sheet_id = self.login_and_create_new_sheet()\n\n        # * He enables json api access for the sheet\n        self.enable_json_api_for_sheet()\n\n        # * He enters some values and formulae\n        self.enter_cell_text(1, 1, '5')\n        self.enter_cell_text(1, 2, 'abc')\n        self.enter_cell_text(2, 1, '6')\n        self.enter_cell_text(3, 1, '=a1 + b1')\n        self.wait_for_cell_value(3, 1, '11')\n        # * He uses an API call to get the sheet as JSON\n        sheet_content = json.load(urlopen(Url.api_url(self.get_my_username(), sheet_id), data=urlencode({'api_key': self.get_my_username()})))\n\n        expected = {\n            'name' : 'Sheet %s' % (sheet_id,),\n            '1': {\n                '1': 5,\n                '2': 'abc'\n                },\n            '2': {\n                '1': 6,\n                },\n            '3': {\n                '1': 11\n            },\n        }\n        self.assertEquals(sheet_content, expected)\n\n\n    def test_simple_json_with_error(self):\n        # * Harold wants to use Dirigible to run his spreadsheets using\n        #   a json-based rest API, and wants the error-handling to be\n        #   well-defined (if perhaps not ideal from a debugging perspective)\n\n\n        # * He logs in to Dirigible and creates a new sheet\n        sheet_id = self.login_and_create_new_sheet()\n\n        # * He enables json api access for the sheet\n        self.enable_json_api_for_sheet()\n\n        # * He enters a single formula operating on a single value\n        self.enter_cell_text(1, 1, '5')\n        self.enter_cell_text(1, 2, '=1/A1')\n        self.wait_for_cell_value(1, 2, '0.2')\n\n        # * He uses an API call to get the sheet as JSON, passing in an override\n        #   value that he knows will cause an error\n        get_params = urlencode({'A1':'0', 'api_key': self.get_my_username()})\n        url = '%s?%s' % (Url.api_url(self.get_my_username(), sheet_id), get_params)\n        try:\n            sheet_content = json.load(urlopen(url))\n        except Exception, e:\n            self.fail(str(e))\n\n        # * He gets back nothing for the cell which generated an error,\n        expected = {\n            'name' : 'Sheet %s' % (sheet_id,),\n            '1': {\n                '1': 0,\n            },\n        }\n        self.assertEquals(sheet_content, expected)\n\n\n    def test_simple_json_with_overrides_get(self):\n        # * Harold wants to use Dirigible to run his spreadsheets using\n        #   a json-based rest API, and override the formula of some cells\n\n        # * He logs in to Dirigible and creates a new sheet\n        sheet_id = self.login_and_create_new_sheet()\n\n        # * He enables json api access for the sheet\n        self.enable_json_api_for_sheet()\n\n        # * He enters some values and formulae\n        self.enter_cell_text(1, 1, '5')         # A1\n        self.enter_cell_text(1, 2, 'abc')       # A2\n        self.enter_cell_text(2, 1, '6')         # B1\n        self.enter_cell_text(3, 1, '=a1 + b1')  # C1\n        self.wait_for_cell_value(3, 1, '11')\n\n        # * He uses an API call to get the sheet as JSON\n        #   but overrides the values of cells using GET params:\n        #       B1=11\n        #       C2=A1 + 1\n        #   this also causes cell C1 to change value, since it\n        #   depends on B1\n        get_params = urlencode({'2,1':'11', 'C2':'=A1 + 1', 'api_key': self.get_my_username()})\n        url = '%s?%s' % (Url.api_url(self.get_my_username(), sheet_id), get_params)\n        try:\n            sheet_content = json.load(urlopen(url))\n        except Exception, e:\n            self.fail(str(e))\n\n        expected = {\n            'name' : 'Sheet %s' % (sheet_id,),\n            '1': {\n                '1': 5,\n                '2': 'abc'\n                },\n            '2': {\n                '1': 11,\n                },\n            '3': {\n                '1': 16,\n                '2': 6\n            },\n        }\n        self.assertEquals(sheet_content, expected)\n\n\n    def test_simple_json_with_overrides_post(self):\n        # * Harold wants to use Dirigible to run his spreadsheets using\n        #   a json-based rest API, and override the formula of some cells\n\n        # * He logs in to Dirigible and creates a new sheet\n        sheet_id = self.login_and_create_new_sheet()\n\n        # * He enables json api access for the sheet\n        self.enable_json_api_for_sheet()\n\n        # * He enters some values and formulae\n        self.enter_cell_text(1, 1, '5')         # A1\n        self.enter_cell_text(1, 2, 'abc')       # A2\n        self.enter_cell_text(2, 1, '6')         # B1\n        self.enter_cell_text(3, 1, '=a1 + b1')  # C1\n        self.wait_for_cell_value(3, 1, '11')\n\n        # * He uses an API call to get the sheet as JSON\n        #   but overrides the values of cells using POST params:\n        #       B1=11\n        #       C2=A1 + 1\n        #   this also causes cell C1 to change value, since it\n        #   depends on B1\n        url = Url.api_url(self.get_my_username(), sheet_id)\n        post_params = urlencode({'2,1':'11', 'C2':'=A1 + 1', 'api_key': self.get_my_username()})\n        try:\n            sheet_content = json.load(urlopen(url, data=post_params))\n        except Exception, e:\n            self.fail(str(e))\n\n        expected = {\n            'name' : 'Sheet %s' % (sheet_id,),\n            '1': {\n                '1': 5,\n                '2': 'abc'\n                },\n            '2': {\n                '1': 11,\n                },\n            '3': {\n                '1': 16,\n                '2': 6\n            },\n        }\n        self.assertEquals(sheet_content, expected)\n\n"
  },
  {
    "path": "dirigible/fts/tests/test_2535_RunWorksheetSerial.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\n\nfrom functionaltest import FunctionalTest\n\n\nclass Test_2535_RunWorksheet(FunctionalTest):\n\n    def test_run_worksheet_no_overrides(self):\n        # * Harold logs in, creates a new sheet and notes its id\n        self.login_and_create_new_sheet()\n        target_sheet_url = self.browser.current_url\n\n        # * and enters a few calculations and constants\n        self.enter_cell_text(1, 1, '2')\n        self.enter_cell_text(1, 2, '3')\n        self.enter_cell_text(3, 3, '=A1 + A2')\n\n        # * He creates another new sheet\n        self.create_new_sheet()\n\n        # * He enters =run_worksheet('http://...') into A1\n        self.enter_cell_text(1, 1, '=run_worksheet(\"%s\")' % (target_sheet_url,))\n\n        # * He enters =A1[3, 3] into A2 and notes that\n        self.enter_cell_text(1, 2, '=A1[3, 3].value')\n\n        #   the value from C3 in the first sheet appears there\n        self.wait_for_cell_value(1, 2, '5')\n\n\n    def test_run_worksheet_with_overrides(self):\n        # * Harold logs in, creates a new sheet and notes its id\n        self.login_and_create_new_sheet()\n        target_sheet_url = self.browser.current_url\n\n        # * and enters a few calculations and constants\n        self.enter_cell_text(1, 1, '2')\n        self.enter_cell_text(1, 2, '3')\n        self.enter_cell_text(3, 3, '=A1 + A2')\n\n        # * He creates another new sheet\n        self.create_new_sheet()\n\n        # * He enters a run_worksheet with overrides into A1\n        overrides = {\n            (1, 1): 10,\n            (1, 2): '43',\n            (1, 3): '=str(A1)'\n        }\n        overrides_in_formula = repr(overrides).replace(':', '->')\n        self.enter_cell_text(1, 1, '=run_worksheet(\"%s\", %s)' % (target_sheet_url, overrides_in_formula))\n\n\n        #   the value from C3 in the first sheet appears there\n        # * He enters =A1[3, 3] into A2 and notes that\n        self.enter_cell_text(1, 2, '=A1[3, 3].value')\n        self.wait_for_cell_value(1, 2, '53')\n\n        # * He also notes that A3 contains 10\n        self.enter_cell_text(2, 2, '=A1[1, 3].value')\n        self.wait_for_cell_value(2, 2, '10')\n\n\n    def test_run_worksheet_with_error(self):\n        # * Harold logs in, creates a new sheet and notes its id\n        self.login_and_create_new_sheet()\n        target_sheet_url = self.browser.current_url\n\n        # * and enters an error\n        self.prepend_usercode('import sys:')\n\n        # * He creates another new sheet\n        self.create_new_sheet()\n\n        # * He enters a run_worksheet with overrides into A1\n        overrides = {\n            (1, 1): 10,\n            (1, 2): '43',\n            (1, 3): '=str(A1)'\n        }\n        overrides_in_formula = repr(overrides).replace(':', '->')\n        self.enter_cell_text(1, 1, '=run_worksheet(\"%s\", %s)' % (target_sheet_url, overrides_in_formula))\n\n        # ... and notes the error shown in A1\n        self.assert_cell_has_error(1, 1, 'Exception: run_worksheet: Syntax error at character 11')\n"
  },
  {
    "path": "dirigible/fts/tests/test_2536_ParallelFormulaExecution.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom textwrap import dedent\nimport re\n\ntry:\n    import unittest2 as unittest\nexcept ImportError:\n    import unittest\n\nfrom functionaltest import FunctionalTest, snapshot_on_error\n\ntook_regex = re.compile('Took ([^s]+)s')\n\nclass Test_2536_ParallelFormulaExecution(FunctionalTest):\n\n    def get_last_recalc_time(self):\n        match = took_regex.match(self.get_console_content())\n        return float(match.group(1))\n\n    @snapshot_on_error\n    def test_formulae_executed_in_parallel(self):\n        # * Harold logs in and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * Harold enters some usercode to provide formulae that take time to calculate\n        self.prepend_usercode(dedent('''\n        import time\n        def slow_product(val1, val2):\n            time.sleep(1)\n            return val1 * val2\n        '''))\n\n        # * he enters a tree of dependent cell formula which use slow_product\n        self.enter_cell_text(1, 3, '=slow_product(1, 2)')\n        self.enter_cell_text(2, 3, '=slow_product(3, 4)')\n        self.enter_cell_text(3, 3, '=slow_product(5, 6)')\n        self.enter_cell_text(4, 3, '=slow_product(7, 8)')\n\n        self.enter_cell_text(1, 2, '=slow_product(A3, B3)')\n        self.enter_cell_text(2, 2, '=slow_product(C3, D3)')\n\n        self.enter_cell_text(1, 1, '=slow_product(A2, B2)')\n\n        # * and waits for the sheet to calculate\n        self.wait_for_cell_value(1, 1, '40320', timeout_seconds=10)\n\n        # * He notes the the recalculation was much faster than he would\n        #   expect if the formulae had been run in series\n        # Note: There are 7 formulae in 3 dependency 'layers' so we expect\n        # the recalc to take about 3sec.\n        self.assertTrue(\n            self.get_last_recalc_time() < 3.2,\n            'calculation took too long - %ss. Parallel broken?' % (self.get_last_recalc_time(),)\n        )\n\n    @snapshot_on_error\n    def test_run_worksheet_executed_in_parallel(self):\n        # * Harold logs in and creates a blank worker sheet\n        self.login_and_create_new_sheet()\n        worker_sheet_url = self.browser.current_url\n\n        # * He then goes to create a master sheet that makes several calls on the worker\n        self.create_new_sheet()\n        master_sheet_url = self.browser.current_url\n\n        # * he enters formulae that use the worksheet in a number of run_worksheet calls\n        run_worksheet_formula = '=run_worksheet(\"%s\")[1, 1].value' % (worker_sheet_url,)\n        for row in range(1, 5):\n            self.enter_cell_text(1, row, run_worksheet_formula)\n\n\n        # * Harold goes back to the worker and enters some usercode to make a recalc slow\n        self.go_to_url(worker_sheet_url)\n        self.wait_for_grid_to_appear()\n\n        self.prepend_usercode(dedent('''\n        import time\n        start = time.time()\n        x = 1\n        while time.time() - start < 10:\n            time.sleep(0.5)\n            x += 1\n        worksheet[1, 1].value = x\n        '''))\n        self.wait_for_spinner_to_stop(timeout_seconds=15)\n\n        # * He now goes back to the master sheet,\n        self.go_to_url(master_sheet_url)\n        self.wait_for_grid_to_appear()\n\n        # * He notes the the recalculation was much faster than he would\n        #   expect if the run_worksheets had been run in series\n        self.wait_for_spinner_to_stop(timeout_seconds=15)\n        self.assertTrue(self.get_last_recalc_time() < 19, 'calculation took too long. Parallel recalc broken?')\n\n"
  },
  {
    "path": "dirigible/fts/tests/test_2537_ErrorsInConsole.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\nfrom functionaltest import FunctionalTest\n\nfrom textwrap import dedent\n\nclass Test_2537_ErrorsInConsole(FunctionalTest):\n\n    def test_console(self):\n        # * Harold logs in and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He notes that there is a console on the page\n        self.is_element_present('id=id_console')\n\n        original_usercode = self.get_usercode()\n\n        # * He makes an error in usercode\n        self.prepend_usercode(dedent('''\n        def fn():\n            x = new_value\n        '''))\n        self.append_usercode(dedent('''\n        fn()'''))\n        fn_call_line = len(self.get_usercode().split('\\n'))\n\n        expected_traceback = '''NameError: global name 'new_value' is not defined\n    User code line %d\n    User code line 2, in fn''' % (fn_call_line,)\n\n        # * ... and notes that a useful traceback appears in the console\n        self.wait_for_console_content(expected_traceback)\n\n        # * He removes his error, saves his usercode and notes that the error is no longer shown\n        self.enter_usercode(original_usercode)\n        self.wait_for_console_content(\"\")\n\n\n    def test_formula_error_in_console(self):\n        # * Harold logs in and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He enters some formulae that contain errors\n        self.enter_cell_text(1, 1, '=1/0')\n        self.enter_cell_text(3, 2, '=newvalue')\n        self.enter_cell_text(2, 4, '={\"a\": 2}')\n        self.assert_cell_has_error(1, 1, 'ZeroDivisionError: float division')\n\n        # * He notes that the errors are reported in the console\n        expected_tracebacks = ('''NameError: name 'newvalue' is not defined\n    Formula '=newvalue' in C2''',\n        '''ZeroDivisionError: float division\n    Formula '=1/0' in A1''',\n        '''FormulaError: Error in formula at position 6: unexpected ': '\n    Formula '={\"a\": 2}' in B4''')\n        for expected_traceback in expected_tracebacks:\n            self.wait_for_console_content(expected_traceback)\n\n        # * He removes the errors and notes that the traceback is cleared\n        self.enter_cell_text(1, 1, '=1')\n        self.enter_cell_text(3, 2, '')\n        self.enter_cell_text(2, 4, '={\"a\"-> 2}')\n        self.wait_for_cell_value(1, 1, '1')\n        self.wait_for_console_content(\"\")\n\n\n    def test_formula_errors_precede_usercode_errors_in_console(self):\n        # * Harold logs in and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He enters a formula with an error\n        self.enter_cell_text(1, 1, '=1/0')\n\n        # * and breaks the usercode after the formulae are run\n        self.append_usercode('x = y')\n        error_line = len(self.get_usercode().split('\\n'))\n\n        # * and notes that the errors are reported in the expected order\n        expected_traceback = '''ZeroDivisionError: float division\n    Formula '=1/0' in A1\nNameError: name 'y' is not defined\n    User code line %d''' % (error_line,)\n        self.wait_for_console_content(expected_traceback)\n\n"
  },
  {
    "path": "dirigible/fts/tests/test_2538_ShowStdoutInConsole.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\n\nfrom functionaltest import FunctionalTest\nfrom textwrap import dedent\n\n\nclass Test_2538_ShowStdoutInConsole(FunctionalTest):\n\n    def test_stdout_displayed_in_console(self):\n        # * Harold logs in and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # He adds usercode to print a message\n        self.prepend_usercode(\"print 'greetings'\")\n        self.append_usercode(\"print 'puny human'\")\n        self.wait_for_spinner_to_stop()\n\n        expected = dedent('''\n        greetings\n        puny human''')[1:]\n        self.wait_for_console_content(expected)\n\n\n    def test_output_interleaved_with_formula_errors(self):\n        # * Harold logs in and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He enters usercode that executes evaluate_formulae and outputs a number of times\n        self.enter_cell_text(1, 1, '=1/0')\n        self.enter_usercode(dedent('''\n            for x in range(2):\n                print 'at %d' % (x,)\n                evaluate_formulae(worksheet)\n        '''))\n        self.wait_for_spinner_to_stop()\n\n        # * He notes that formula errors and output are correctly interleaved\n        self.wait_for_console_content(dedent('''\n        at 0\n        ZeroDivisionError: float division\n            Formula '=1/0' in A1\n        at 1\n        ZeroDivisionError: float division\n            Formula '=1/0' in A1''')[1:])\n\n        #   and that they are correctly coloured\n        self.assertEquals(\n            self.sanitise_console_content(self.selenium.get_eval(\"window.$('.console_output_text').text()\")),\n            dedent('''\n                at 0\n                at 1\n            ''')[1:]\n            )\n\n        self.assertEquals(\n            self.sanitise_console_content(self.selenium.get_eval(\"window.$('.console_error_text').text()\")),\n            dedent('''\n                ZeroDivisionError: float division\n                    Formula '=1/0' in A1\n                ZeroDivisionError: float division\n                    Formula '=1/0' in A1\n            ''')[1:]\n        )\n\n"
  },
  {
    "path": "dirigible/fts/tests/test_2540_FrontPage.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\n\nfrom functionaltest import FunctionalTest, Url\n\nfrom browser_settings import SERVER_IP\n\n\nimport urllib2\nimport urlparse\n\nclass Test_2540_FrontPage(FunctionalTest):\n\n    def check_url_not_broken(self, url):\n        try:\n            response = urllib2.urlopen(url)\n        except Exception, e:\n            self.fail(\"Error getting url %s: %s\" % (url, str(e)))\n\n\n    def check_links_not_broken_for_tag_attribute(self, tag_xpath, attribute):\n        num_tags = self.selenium.get_xpath_count(tag_xpath)\n        base_file_location = self.browser.current_url\n        for i in range(1, num_tags + 1):\n            src = self.selenium.get_attribute(\"xpath=(%s)[%s]@%s\" % (tag_xpath, i, attribute))\n            url = urlparse.urljoin(base_file_location, src)\n            if not url.startswith(\"mailto:\"):\n                self.check_url_not_broken(url)\n\n\n    def test_front_page_links(self):\n        # Harold goes to Dirigible's root page\n        self.go_to_url('http://%s/' % (SERVER_IP,))\n\n        # He finds a page with the title  \"Welcome to Dirigible\"\n        self.assertEquals(self.browser.title, 'Welcome to Dirigible')\n\n        # The CSS files are all loaded correctly\n        self.check_links_not_broken_for_tag_attribute(\"//link[@rel='stylesheet']\", \"href\")\n\n        # As are the images\n        self.check_links_not_broken_for_tag_attribute(\"//img\", \"src\")\n\n        # And the links\n        self.check_links_not_broken_for_tag_attribute(\"//a\", \"href\")\n\n        # He notes in particular that the \"log in\" link take him to the login page.\n        front_page = self.browser.current_url\n        login_url = self.selenium.get_attribute(\"xpath=//a[text() = 'Log in']@href\")\n        self.assert_sends_to_login_page(login_url)\n        self.go_to_url(front_page)\n\n        # ...and that the \"Pricing\", \"Find out more\", \"Watch a video\", \"Help\", \"Terms & Conditions\",\n        # \"Privacy Policy\", and \"Contact Us\" take him to pages which have appropriate-looking titles.\n        self.assert_page_title_contains(\n            self.selenium.get_attribute(\"xpath=//a[@id='id_pricing_link']@href\"),\n            'Dirigible Pricing'\n        )\n        self.assert_page_title_contains(\n            self.selenium.get_attribute(\"xpath=//a[img[@id='id_find_out_more']]@href\"),\n            'About Dirigible'\n        )\n        self.assert_page_title_contains(\n            self.selenium.get_attribute(\"xpath=//a[img[@id='id_watch_a_video']]@href\"),\n            'The Video: Dirigible'\n        )\n        self.assert_page_title_contains(\n            self.selenium.get_attribute(\"xpath=//a[text() = 'Help']@href\"),\n            'Dirigible documentation'\n        )\n        self.assert_has_useful_information_links()"
  },
  {
    "path": "dirigible/fts/tests/test_2544_403_404_and_500_pages.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom urlparse import urljoin\n\nfrom functionaltest import FunctionalTest, PAGE_LOAD_TIMEOUT, Url\n\n\nclass Test_2544_404And500Pages(FunctionalTest):\n\n    user_count = 2\n\n\n    def test_403(self):\n        harriet = self.get_my_usernames()[1]\n        self.login_and_create_new_sheet(harriet)\n        sheet_url = self.browser.current_url\n        self.logout()\n\n        # * Harold goes to a page he should not have access to\n        harold = self.get_my_username()\n        self.login(harold)\n        ## Older versions of Selenium (like the one we use for IE) signal errors\n        ## with exceptions.  Newer ones don't.  We handle the former case by\n        ## looking at the exception details; for the latter we rely on the checks\n        ## of the page text below.\n        try:\n            self.selenium.open(sheet_url)\n        except Exception, e:\n            self.assertTrue(\"403\" in str(e), \"Error not a 403: %s\" % (e,))\n\n        # * He gets an appropriate Dirigible-specific 403 page.\n        self.selenium.wait_for_page_to_load(PAGE_LOAD_TIMEOUT)\n        self.check_page_load(sheet_url)\n        self.assertEquals(self.browser.title, \"Access Forbidden: Dirigible\")\n        self.assertEquals(self.get_text(\"id=id_server_error_title\"), \"403 Access Forbidden\")\n        error_text = self.get_text(\"id=id_server_error_text\")\n        self.assertTrue(\"This page is private and belongs to someone else.\" in error_text)\n\n        # There is also a link to the Dirigible home page, which he follows and discovers\n        # that it works.\n        self.click_link('id_link_home')\n        self.assertEquals(self.browser.current_url, Url.ROOT)\n\n\n\n    def test_404(self):\n        # * Harold goes to a non-existent page\n        url = urljoin(Url.ROOT, '/notaVALIDpage/')\n        ## Older versions of Selenium (like the one we use for IE) signal errors\n        ## with exceptions.  Newer ones don't.  We handle the former case by\n        ## looking at the exception details; for the latter we rely on the checks\n        ## of the page text below.\n        try:\n            self.selenium.open(url)\n        except Exception, e:\n            self.assertTrue(\"404\" in str(e), \"Error not a 404: %s\" % (e,))\n\n        # * He gets an appropriate Dirigible-specific 404 page.\n        self.selenium.wait_for_page_to_load(PAGE_LOAD_TIMEOUT)\n        self.check_page_load(url)\n        self.assertEquals(self.browser.title, \"Page not found: Dirigible\")\n        self.assertEquals(self.get_text(\"id=id_server_error_title\"), \"404 Not Found\")\n        error_text = self.get_text(\"id=id_server_error_text\")\n        self.assertTrue(\"The page that you seek\" in error_text)\n        self.assertTrue(\"can't be found; lost in the clouds.\" in error_text)\n        self.assertTrue(\"Please look somewhere else\" in error_text)\n\n        # There is a link to the Dirigible home page, which he follows and discovers\n        # that it works.\n        self.click_link('id_link_home')\n        self.assertEquals(self.browser.current_url, Url.ROOT)\n\n\n\n    def test_500(self):\n        # * Harold somehow triggers an internal server error\n        self.login_and_create_new_sheet()\n        url = urljoin(self.browser.current_url, 'set_cell_formula/')\n        ## Older versions of Selenium (like the one we use for IE) signal errors\n        ## with exceptions.  Newer ones don't.  We handle the former case by\n        ## looking at the exception details; for the latter we rely on the checks\n        ## of the page text below.\n        try:\n            self.selenium.open(url)\n        except Exception, e:\n            self.assertTrue(\"500\" in str(e), \"Error not a 500: %s\" % (e,))\n\n        # * He gets an appropriate Dirigible-specific 500 page.\n        self.selenium.wait_for_page_to_load(PAGE_LOAD_TIMEOUT)\n        self.check_page_load(url)\n        self.assertEquals(self.browser.title, \"Server error: Dirigible\")\n        self.assertEquals(self.get_text(\"id=id_server_error_title\"), \"500 Internal Server Error\")\n        error_text = self.get_text(\"id=id_server_error_text\")\n        self.assertTrue(\"Sorry, something has gone wrong!\" in error_text)\n        self.assertTrue(\"The Dirigible team have notified and will look into the problem ASAP.\" in error_text)\n\n        # There is a link to the Dirigible home page, which he follows and discovers\n        # that it works.\n        self.click_link('id_link_home')\n        self.assertEquals(self.browser.current_url, Url.ROOT)\n"
  },
  {
    "path": "dirigible/fts/tests/test_2545_PageResizeBehaviour.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\r\n# All Rights Reserved\r\n#\r\n\r\n\r\nfrom functionaltest import FunctionalTest, snapshot_on_error\r\n\r\n\r\nclass Test_2545_PageResizeBehaviour(FunctionalTest):\r\n\r\n    @snapshot_on_error\r\n    def test_splitters(self):\r\n        # Harold logs in and creates a new sheet.\r\n        self.login_and_create_new_sheet()\r\n\r\n        # He notes that there are splitter bars\r\n        self.assertTrue(self.is_element_present('css=.hsplitbar'))\r\n        self.assertTrue(self.is_element_present('css=.vsplitbar'))\r\n\r\n        # He uses the vertical split bar to resize the grid to its minimum width\r\n        self.selenium.drag_and_drop('css=.vsplitbar', '-4000, 0')\r\n        self.assertAlmostEquals(self.selenium.get_element_width('id=id_left_column'), 0, delta=5)\r\n        # and then increases its width a bit\r\n        self.selenium.drag_and_drop('css=.vsplitbar', '40, 0')\r\n        self.assertAlmostEquals(self.selenium.get_element_width('id=id_left_column'), 40, delta=5)\r\n\r\n        # He uses the horizontal split bar to shrink the console\r\n        self.selenium.drag_and_drop('css=.hsplitbar', '0, 2000')\r\n        self.assertAlmostEquals(self.selenium.get_element_height('id=id_console_wrap'), 0, delta=5)\r\n        # and then increases its height a bit\r\n        self.selenium.drag_and_drop('css=.hsplitbar', '0, -20')\r\n        self.assertAlmostEquals(self.selenium.get_element_height('id=id_console_wrap'), 20, delta=5)\r\n\r\n        # He notes that his splitter positions are restored when he reloads the page\r\n        self.refresh_sheet_page()\r\n        self.wait_for_grid_to_appear()\r\n        self.assertAlmostEquals(self.selenium.get_element_width('id=id_left_column'), 40, delta=5)\r\n        self.assertAlmostEquals(self.selenium.get_element_height('id=id_console_wrap'), 20, delta=5)\r\n\r\n        # He uses the vertical split bar to resize the right-hand column to its minimum width\r\n        self.selenium.drag_and_drop('css=.vsplitbar', '4000, 0')\r\n        self.assertAlmostEquals(self.selenium.get_element_width('id=id_right_column'), 0, delta=5)\r\n        # and then increases its width a bit\r\n        self.selenium.drag_and_drop('css=.vsplitbar', '-40, 0')\r\n        self.assertAlmostEquals(self.selenium.get_element_width('id=id_right_column'), 40, delta=5)\r\n\r\n        # He uses the horizontal split bar to shrink the editor to zero size, and notes that it disappears\r\n        self.selenium.drag_and_drop('css=.hsplitbar', '0, -2000')\r\n        self.wait_for_element_visibility('id=id_usercode', False)\r\n\r\n        # He uses the horizontal split bar to make the editor 25px high, and notes that it is still hidden\r\n        self.selenium.drag_and_drop('css=.hsplitbar', '0, 25')\r\n        self.wait_for_element_visibility('id=id_usercode', False)\r\n\r\n        # He uses the horizontal split bar to give the editor and its margins 50px of space, and notes\r\n        # that it is visible and small\r\n        self.selenium.drag_and_drop('css=.hsplitbar', '0, 25')\r\n        self.wait_for_element_visibility('id=id_usercode', True)\r\n        self.assertAlmostEquals(self.selenium.get_element_height('id=id_usercode'), 40, delta=5)\r\n\r\n\r\n    @snapshot_on_error\r\n    def test_grid_resize(self):\r\n        # Harold logs in a creates a new sheet.\r\n        self.login_and_create_new_sheet()\r\n\r\n        # He sees that the bottom of the grid is a specific distance from the bottom of the window,\r\n        # and that the top of the grid is a specific distance from the top of the window.  He notes\r\n        # those distances down.\r\n        grid_bounds = self.get_element_bounds(\"id=id_grid\")\r\n        browser_page_height = int(self.selenium.get_eval(\"window.document.body.clientHeight\"))\r\n        browser_page_width = int(self.selenium.get_eval(\"window.document.body.clientWidth\"))\r\n        original_distance_to_bottom = browser_page_height - grid_bounds.bottom\r\n        original_distance_to_top = grid_bounds.top\r\n\r\n        # He resizes the window.\r\n        self.selenium.get_eval(\"window.resizeTo(%s, %s)\" % (browser_page_width - 10, browser_page_height - 10))\r\n\r\n        # He notes that the grid is still the same distances from the top and the bottom of the\r\n        # window.\r\n        def get_distance_to_bottom():\r\n            new_grid_bounds = self.get_element_bounds(\"id=id_grid\")\r\n            browser_page_height = int(self.selenium.get_eval(\"window.document.body.clientHeight\"))\r\n            return browser_page_height - new_grid_bounds.bottom\r\n        self.wait_for(\r\n            lambda: get_distance_to_bottom() == original_distance_to_bottom,\r\n            lambda: \"Distance to bottom to become %s (was %s)\" % (original_distance_to_bottom, get_distance_to_bottom())\r\n        )\r\n\r\n        def get_distance_to_top():\r\n            return self.get_element_bounds(\"id=id_grid\").top\r\n        self.wait_for(\r\n            lambda: get_distance_to_top() == original_distance_to_top,\r\n            lambda: \"Distance to top to become %s (was %s)\" % (original_distance_to_top, get_distance_to_top())\r\n        )\r\n"
  },
  {
    "path": "dirigible/fts/tests/test_2546_ListSheetsOnDashboard.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom functionaltest import FunctionalTest, SERVER_IP, snapshot_on_error\n\nimport key_codes\n\n\nclass Test_2546_ListSheetsOnDashboard(FunctionalTest):\n\n    def rename_current_sheet(self, name):\n        self.selenium.click('id=id_sheet_name')\n        self.wait_for(\n            lambda: self.is_element_present('id=edit-id_sheet_name'),\n            lambda: 'edit sheetname textbox to appear')\n        self.selenium.type('id=edit-id_sheet_name', name)\n        self.human_key_press(key_codes.ENTER)\n        self.wait_for_element_presence('id=saving-id_sheet_name', False)\n\n\n    def assert_sheet_is_listed(self, sheet_id, sheet_name=None):\n        if sheet_name is None:\n            sheet_name = 'Sheet %s' % (sheet_id,)\n        expected_url = '/user/%s/sheet/%s/' % (self.get_my_username(), sheet_id)\n        link_text = self.get_text(\n            'css=a[href=\"%s\"]' % (expected_url,))\n            # \"xpath=//a[contains(@href, '%s')]\" % (expected_url,))\n        self.assertEquals(link_text, sheet_name)\n\n\n    @snapshot_on_error\n    def test_list_exists(self):\n        # * Harold logs in to Dirigible.\n        self.login()\n\n        # * He notes that he is being told that he has no sheets.\n        self.assertTrue(\n            self.is_element_present('id=id_no_sheets_message'),\n            \"Could not find 'no sheets' message\"\n        )\n\n        # * He decides that he wants one, so he clicks on a button to create it.\n        sheet1_id = self.create_new_sheet()\n\n        # * He clicks 'my account' and goes back to dashboard page\n        # * His new sheet is listed there, with a link to it\n        self.click_link('id_account_link')\n        self.assert_sheet_is_listed(sheet1_id)\n\n        # He notes that the \"no sheets\" message is absent.\n        self.assertFalse(\n            self.is_element_present('id=id_no_sheets_message'),\n            \"Found 'no sheets' message when it wasn't expected\"\n        )\n\n\n        # * He clicks new sheet again\n        sheet2_id = self.create_new_sheet()\n        # * He renames the second sheet\n        self.rename_current_sheet('Snarf')\n\n        # * He clicks 'my account' and goes back to dashboard page\n        self.click_link('id_account_link')\n        # * Both sheets are listed there\n        self.assert_sheet_is_listed(sheet1_id)\n        self.assert_sheet_is_listed(sheet2_id, 'Snarf')\n\n"
  },
  {
    "path": "dirigible/fts/tests/test_2547_EnterDataQuickly.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nimport time\n\nfrom functionaltest import FunctionalTest, snapshot_on_error\n\n\nclass Test_2547_EnterDataQuickly(FunctionalTest):\n\n    @snapshot_on_error\n    def test_enter_data_quickly(self):\n        # * Harold logs in and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He enters 20 numbers quickly\n        for row in range(1, 21):\n            self.enter_cell_text_unhumanized(1, row, \"%s\" % (row,))\n\n        # * He checks that they are all there.\n        for row in range(1, 21):\n            self.wait_for_cell_value(1, row, \"%s\" % (row,))\n\n\n    @snapshot_on_error\n    def test_enter_data_quickly_in_batches(self):\n        # Running the above test made it clear that it was significantly more\n        # likely to drop the last number than any others, so we added the below\n        # to increase the likelihood of that particular failure mode.\n\n        # * Harold logs in and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He enters 20 numbers quickly, taking a brief rest every five numbers\n        for row in range(1, 21):\n            self.enter_cell_text_unhumanized(1, row, \"%s\" % (row,))\n            if row % 5 == 0:\n                time.sleep(2)\n\n        # * He checks that they are all there.\n        for row in range(1, 21):\n            self.wait_for_cell_value(1, row, \"%s\" % (row,))\n\n"
  },
  {
    "path": "dirigible/fts/tests/test_2548_UserCode.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom textwrap import dedent\n\nfrom functionaltest import FunctionalTest, snapshot_on_error\n\n\nclass Test_2548_UserCode(FunctionalTest):\n\n    @snapshot_on_error\n    def test_editor_presence(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He notices that there is an Ace code editor on the page, which he\n        #   has inexplicably not noticed before.\n        self.assertTrue(\n            self.is_element_present('css=#id_usercode.ace_editor')\n        )\n\n        # * In the editor, there is some interesting-looking code\n        self.assertEquals(\n            self.get_usercode().strip(),\n            dedent(\n                '''\n                load_constants(worksheet)\n\n                # Put code here if it needs to access constants in the spreadsheet\n                # and to be accessed by the formulae.  Examples: imports,\n                # user-defined functions and classes you want to use in cells.\n\n                evaluate_formulae(worksheet)\n\n                # Put code here if it needs to access the results of the formulae.\n                ''').strip()\n        )\n\n\n    @snapshot_on_error\n    def test_preformula_usercode(self):\n        # * Harold wants to create a function to use in a cell's formula\n        # * He logs in to Dirigible and creates a new sheet.\n        self.login_and_create_new_sheet()\n\n        # * He enters the following code into the usercode before the formula code:\n        #{{{\n        #def addA1(value):\n        #    return value + worksheet.A1\n        #}}}\n        self.enter_usercode(dedent('''\n            load_constants(worksheet)\n\n            def addA1(value):\n                return value + worksheet[1, 1].value\n\n            evaluate_formulae(worksheet)\n        '''))\n\n        # * He enters '1' into A1\n        self.enter_cell_text(1, 1, '1')\n\n        # * In cell A4, he enters \"=addA1(5)\", and gets the result 6.\n        self.enter_cell_text(1, 4, '=addA1(5)')\n        self.wait_for_cell_value(1, 4, '6')\n\n\n    @snapshot_on_error\n    def test_postformula_usercode(self):\n        # * Harold wants to specify code to run after his formulae have been calculated\n        # * He logs in to Dirigible and creates a new sheet.\n        self.login_and_create_new_sheet()\n\n        # * He enters 2 into cell A3\n        self.enter_cell_text(1, 3, '2')\n\n        # * He adds an appropriate line to the end of the usercode, after the\n        #   evaluate_formulae call, and applies the change:\n        self.enter_usercode(dedent('''\n            load_constants(worksheet)\n\n            evaluate_formulae(worksheet)\n\n            worksheet[1, 5].value = 10 + worksheet[1, 3].value\n        '''))\n\n        # * He notes that the value \"13\" appears in A5.\n        self.wait_for_cell_value(1, 5, '12')\n"
  },
  {
    "path": "dirigible/fts/tests/test_2549_InterruptedRecalculations.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nimport time\nfrom textwrap import dedent\n\nfrom functionaltest import FunctionalTest, snapshot_on_error\n\n\nclass Test_2549_InterruptedRecalculations(FunctionalTest):\n\n    @snapshot_on_error\n    def test_interrupted_recalc_coming_back_handled_correctly(self):\n        # Harold logs on and creates a sheet\n        self.login_and_create_new_sheet()\n\n        # Harold enters a long-running recalc that finishes by\n        # setting a value in the grid\n        self.enter_usercode(dedent('''\n            import time\n            time.sleep(20)\n            worksheet[1, 5].value = 1\n        '''))\n\n        # While it is running, he realises that there is a shorter\n        # algorithm, so he tries that instead.\n        time.sleep(1)\n        self.enter_usercode(dedent('''\n            worksheet[1, 5].value = 2\n        '''))\n\n        # Even though the initial longer-running recalc finishes\n        # after the second one, the result he sees is from the\n        # second one; he concludes that the results of the first\n        # one must have been thrown away.\n        self.wait_for_cell_value(1, 5, '2')\n\n        # To confirm this to himself, he waits until the first\n        # recalc has absolutely definitely finished, and confirms\n        # that its results never appear in the grid.\n        time.sleep(30)\n        self.wait_for_cell_value(1, 5, '2')\n\n        # He then refreshes his web page to make sure that the\n        # correct version is current in the database.\n        self.refresh_sheet_page()\n        self.wait_for_grid_to_appear()\n        self.wait_for_cell_value(1, 5, '2')\n"
  },
  {
    "path": "dirigible/fts/tests/test_2550_EditableSheetName.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\n\nfrom functionaltest import FunctionalTest\nfrom textwrap import dedent\n\nimport key_codes\n\n\nclass Test_2550_EdittableSheetName(FunctionalTest):\n\n    def test_sheet_name_edittable(self):\n        # * Harold logs in and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He notes that the sheet has a name of the form 'Sheet #xx'\n        #   where xx is the sheet id\n        sheet_id = self.browser.current_url.split(r'/')[-2]\n        self.assertEquals(\n            self.get_text('id=id_sheet_name'),\n            'Sheet %s' % (sheet_id,))\n\n        # * He mouses over the sheet name and notes that the appearance\n        #   changes to indicate that it's editable\n        self.selenium.mouse_over('id=id_sheet_name')\n        self.wait_for(\n            lambda: self.get_css_property('#id_sheet_name', 'background-color') == '#D1D2D4',\n            lambda: 'sheet name background to darken')\n\n        # * He moves the mouse away - it changes back\n        self.selenium.mouse_out('id=id_sheet_name')\n        self.wait_for(\n            lambda: self.get_css_property('#id_sheet_name', 'background-color') == 'transparent',\n            lambda: 'sheet name background to return to normal')\n\n        # * He clicks on the sheet name, the sheeetname edit textarea appears,\n        # and remains when the mouse moves away\n        self.selenium.click('id=id_sheet_name')\n        self.wait_for(\n            lambda: self.is_element_present('id=edit-id_sheet_name'),\n            lambda: 'editable sheetname to appear')\n        self.selenium.mouse_out('id=id_sheet_name')\n        self.assertTrue(self.is_element_present('id=edit-id_sheet_name'))\n\n        # * He types a new sheetname 'margarita', hits return.\n        self.selenium.type('id=edit-id_sheet_name', 'margarita')\n        self.human_key_press(key_codes.ENTER)\n\n        #   The sheetname is modified\n        self.wait_for(\n            lambda: self.get_text('id=id_sheet_name') == 'margarita',\n            lambda: 'sheet name to be updated'\n        )\n        # and the title to the page becomes \"user's sheet_name: Dirigible\"\n        self.assertEquals(self.browser.title, \"%s's %s: Dirigible\" %\n            (self.get_my_username(), 'margarita'))\n\n        # * He refreshes the page\n        self.refresh_sheet_page()\n\n        #   The new sheetname persists\n        self.wait_for(\n            lambda: self.get_text('id=id_sheet_name') == 'margarita',\n            lambda: 'sheet name to be updated'\n        )\n        # and the title to the page remains \"user's sheet_name: Dirigible\"\n        self.wait_for(\n            lambda: self.browser.title == \"%s's %s: Dirigible\" % (self.get_my_username(), 'margarita'),\n            lambda: 'page title to update'\n        )\n\n\n    def test_changing_sheet_name_should_not_interrupt_recalc_but_still_succeeds(self):\n        # * Harold logs in and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He enters usercode that takes a long time to run\n        self.enter_cell_text(1, 1, '')\n        self.enter_usercode(dedent('''\n            import time\n            time.sleep(20)\n            worksheet[1, 1].value = 'recalced'\n        '''))\n\n        # * While the recalc is running, he changes the sheet name\n        self.selenium.click('id=id_sheet_name')\n        self.wait_for(\n            lambda: self.is_element_present('id=edit-id_sheet_name'),\n            lambda: 'editable sheetname to appear')\n        self.selenium.type('id=edit-id_sheet_name', 'new sheet name')\n        self.human_key_press(key_codes.ENTER)\n\n        # * The recalculation finishes normally and his sheet has a new name\n        self.wait_for_cell_value(1, 1, 'recalced', timeout_seconds=21)\n        self.assertEquals(self.get_text('id=id_sheet_name'), 'new sheet name')\n\n        # * he checks that the new sheetname sticks after a page refresh\n        self.refresh_sheet_page()\n        self.assertEquals(self.get_text('id=id_sheet_name'), 'new sheet name')\n\n\n\n"
  },
  {
    "path": "dirigible/fts/tests/test_2554_SlicingInFormulae.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom functionaltest import FunctionalTest\n\n\nclass Test_2554_SlicingInFormulae(FunctionalTest):\n\n    def test_formulas_work_properly(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # Harold REALLY likes slicing things. We should ensure that\n        # he can indulge his axe-murderer side\n        self.enter_cell_text(1, 1, '=[0, 10, 20, 30, 40]')\n        self.enter_cell_text(1, 2, '=A1[1->3]')\n        self.wait_for_cell_value(1, 2, '[10, 20]')\n\n        self.enter_cell_text(2, 1, 'a string that really needs to be sliced')\n        self.enter_cell_text(2, 2, '=B1[->10]')\n        self.wait_for_cell_value(2, 2, 'a string t')\n\n        self.enter_cell_text(3, 2, '=B1[9->]')\n        self.wait_for_cell_value(3, 2, 'that really needs to be sliced')\n\n        self.enter_cell_text(4, 2, '=B1[-8->]')\n        self.wait_for_cell_value(4, 2, 'e sliced')\n\n        self.enter_cell_text(5, 2, '=B1[4->->3]')\n        self.wait_for_cell_value(5, 2, 'rgh ayesoele')\n"
  },
  {
    "path": "dirigible/fts/tests/test_2556_BrokenUserCode.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom functionaltest import FunctionalTest, snapshot_on_error\n\n\nclass test_2556_BrokenUsercode(FunctionalTest):\n\n    @snapshot_on_error\n    def test_newly_entered_formulae_appear_grey_then_results_in_black(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # make recalc slow, so that we can be sure to see cells\n        # passing through their intermediate 'sent to server\n        # but not yet evaluated' state\n        # Set 2,2 to ready to be sure this usercode change is made before\n        # proceeding.\n        self.prepend_usercode('import time\\ntime.sleep(20.0)\\nworksheet[2,2].formula=\"ready\"')\n        self.wait_for_cell_value(2, 2, 'ready', timeout_seconds=22)\n\n        # check intermediate state then final state after entering a formula\n        self.enter_cell_text(1, 2, '=123')\n        self.wait_for_cell_shown_formula(1, 2, '=123', timeout_seconds=19)\n        self.wait_for_cell_value(1, 2, '123', timeout_seconds=30)\n\n\n    def test_broken_usercode_returns_as_much_as_it_can(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        original_usercode = self.get_usercode()\n\n        # * He enters a constant and a formula in the sheet\n        self.enter_cell_text(1, 1, 'abc')\n        self.enter_cell_text(1, 2, '=123')\n        self.wait_for_cell_value(1, 2, '123')\n\n        # * He adds a usercode error before the constants get calculated\n        self.prepend_usercode('x=1/0')\n\n        self.enter_cell_text(2, 1, 'def')\n        self.enter_cell_text(2, 2, '=456')\n        self.wait_for_cell_shown_formula(2, 2, '=456')\n\n        self.assert_cell_shown_formula(1, 1, 'abc')\n        self.assert_cell_shown_formula(1, 2, '=123')\n        self.assert_cell_shown_formula(2, 1, 'def')\n        self.assert_cell_shown_formula(2, 2, '=456')\n\n        # * He adds a usercode error between constants and formulae\n\n        def insert(text):\n            insertion_point = original_usercode.find('evaluate_formulae(worksheet)')\n            return \"%s\\n%s\\n%s\" % (\n                original_usercode[:insertion_point],\n                text,\n                original_usercode[insertion_point:],\n            )\n\n        usercode = insert('x=1/0\\n')\n        self.enter_usercode(usercode)\n\n        self.enter_cell_text(3, 1, 'ghi')\n        self.enter_cell_text(3, 2, '=789')\n        self.wait_for_cell_shown_formula(3, 2, '=789')\n\n        self.assertEquals(self.get_cell_text(1, 1), 'abc')\n        self.assert_cell_shown_formula(1, 2, '=123')\n        self.assertEquals(self.get_cell_text(2, 1), 'def')\n        self.assertEquals(self.get_cell_text(2, 2), '=456')\n        self.assertEquals(self.get_cell_text(3, 1), 'ghi')\n        self.assertEquals(self.get_cell_text(3, 2), '=789')\n\n        # * He adds a usercode error after formulae\n        self.enter_usercode(original_usercode)\n        self.append_usercode('x=1/0')\n\n        self.enter_cell_text(4, 1, 'jkl')\n        self.enter_cell_text(4, 2, '=100')\n        self.wait_for_cell_value(4, 2, '100')\n\n        self.assertEquals(self.get_cell_text(1, 1), 'abc')\n        self.assertEquals(self.get_cell_text(1, 2), '123')\n        self.assertEquals(self.get_cell_text(2, 1), 'def')\n        self.assertEquals(self.get_cell_text(2, 2), '456')\n        self.assertEquals(self.get_cell_text(3, 1), 'ghi')\n        self.assertEquals(self.get_cell_text(3, 2), '789')\n        self.assertEquals(self.get_cell_text(4, 1), 'jkl')\n        self.assertEquals(self.get_cell_text(4, 2), '100')\n\n        # \\o/ hooray!\n\n"
  },
  {
    "path": "dirigible/fts/tests/test_2557_ClickAwaySavesUsercode.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom functionaltest import FunctionalTest\n\n\nclass Test_2557_ClickAwaySavesUsercode(FunctionalTest):\n\n    def test_blur_on_edit_textarea_saves_usercode(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He enters some usercode that sets A1 to 4\n        self.selenium.get_eval('window.editor.focus()')\n        self.enter_usercode('worksheet[1, 1].value = 4')\n\n        # * He does something to cause a blur event on the editarea textbox\n        ## We use fire_event because Selenium does not fire the full event stack\n        ## for clicks. If this changes in future Selenium versions, it would be nice\n        ## to expand this test to check behaviour for clicking on cells, links and\n        ## edit sheet name\n        self.selenium.get_eval(\"window.editor.blur()\")\n\n        # * ... and notes that A1 contains 4\n        self.wait_for_cell_value(1, 1, '4')\n"
  },
  {
    "path": "dirigible/fts/tests/test_2558_MoreCellsByDefault.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\n\nfrom functionaltest import FunctionalTest\n\n\nclass Test_2558_MoreCellsByDefault(FunctionalTest):\n\n    def test_more_cells_by_default(self):\n        # * Harold logs in and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He notes that the sheet he is presented with has\n        #   an elegant sufficiency of rows and columns (1000 rows and 702\n        #   (up to ZZ) columns)\n        self.scroll_cell_row_into_view(1, 1000)\n\n        row_list_xpath = '//div[contains(@class, \"slick-row\") and @row=999]'\n        row_count = self.selenium.get_xpath_count(row_list_xpath)\n        self.assertEquals(row_count, 1, 'fewer than 1000 rows')\n\n        self.scroll_cell_row_into_view(52, 1)\n        column_list_xpath = '//div[@class=\"slick-cell c52\"]'\n\n        column_count = self.selenium.get_xpath_count(column_list_xpath)\n        self.assertTrue(column_count >= 1, 'fewer than AZ columns')\n\n        self.scroll_cell_row_into_view(1, 1)\n        # * The last cell on the grid accepts input as expected\n        self.enter_cell_text(52, 1000, 'end of the sheet')\n\n        # * He notes that, since he just typed on the bottom row,\n        #   the cell stays in edit mode, so he has to click away\n        #   to finish the edit\n        self.click_on_cell(51, 999)\n        self.wait_for_cell_value(52, 1000, 'end of the sheet')\n"
  },
  {
    "path": "dirigible/fts/tests/test_2559_FitEditorToCells.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\n\nfrom functionaltest import FunctionalTest\n\n\nclass Test_2559_FitEditorToCells(FunctionalTest):\n\n    def test_editor_fits_cells(self):\n        # * Harold logs in and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        cell_locator = self.get_cell_locator(3, 3)\n        cell_width = self.selenium.get_element_width(cell_locator)\n        cell_height = self.selenium.get_element_height(cell_locator)\n\n        # * He edits a cell and notes that the editor fits the\n        #   cell exactly\n        self.open_cell_for_editing(3, 3)\n        editor_locator = 'css=input.editor-text'\n        editor_width = self.selenium.get_element_width(editor_locator)\n        editor_height = self.selenium.get_element_height(editor_locator)\n\n        self.assertTrue(cell_width - editor_width <= 6, \"cell width: %d, editor width: %d\" % (cell_width, editor_width))\n        self.assertTrue(cell_height - editor_height <= 6, \"cell height: %d, editor height: %d\" % (cell_height, editor_height))\n"
  },
  {
    "path": "dirigible/fts/tests/test_2562_ErrorInCellShouldBeClearedByConstants.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom functionaltest import FunctionalTest\n\n\nclass test_2562_ErrorInCellShouldBeClearedByConstants(FunctionalTest):\n\n    def test_ConstantsClearCellErrors(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He enters and error in A1.\n        self.enter_cell_text(1, 1, '=1/0')\n        self.assert_cell_has_error(1, 1, \"ZeroDivisionError: division by zero\")\n\n        # * when he overwrites the cell content with a constant\n        #   the error disappears\n        self.enter_cell_text(1, 1, '123')\n        self.assert_cell_has_no_error(1, 1)\n\n        # hooray!\n\n"
  },
  {
    "path": "dirigible/fts/tests/test_2565_JSONAPIAuth.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom __future__ import with_statement\n\ntry:\n    import json\nexcept ImportError:\n    import simplejson as json\nfrom urllib import urlencode\nfrom urllib2 import urlopen, HTTPError\nfrom urlparse import urljoin, urlparse\n\n# these can't be imported on non-windows machines, but we need to import test\n# modules on our *nix integration master without running them, to run the build\ntry:\n    from win32clipboard import OpenClipboard, GetClipboardData, CloseClipboard\n    import win32con\nexcept:\n    pass\n\nimport key_codes\nfrom functionaltest import FunctionalTest, PAGE_LOAD_TIMEOUT, Url\n\n\nclass Test_2565_JSONAPIAuth(FunctionalTest):\n\n    def test_json_api_auth(self):\n        # Harold wants to make sure that people only have JSON access to his sheets\n        # when he has explicitly granted it.\n\n        # * He logs in to Dirigible and creates a new sheet\n        sheet_id = self.login_and_create_new_sheet()\n        base_json_url = urljoin(self.browser.current_url, 'v0.1/json/')\n\n        # * He enters some values and formulae\n        self.enter_cell_text(1, 1, '5')\n\n        # * He tries to use an API call to get the sheet as JSON, but gets a 403 error.\n        with self.assertRaises(HTTPError) as mngr:\n            urlopen(base_json_url)\n        self.assertEquals(mngr.exception.code, 403)\n\n        # * Looking around at the sheet page, he notices a \"Security\" button.\n        self.wait_for_element_to_appear('id=id_security_button')\n\n        # * He sees that the mouseover text on the button indicates that the JSON API is not enabled\n        self.assertTrue(\n            'JSON API disabled' in\n            self.selenium.get_attribute('css=#id_security_button@title')\n        )\n        self.assertTrue(\n            'JSON API disabled' in\n            self.selenium.get_attribute('css=#id_security_button@alt')\n        )\n\n        # * He clicks the button.\n        self.selenium.click('id=id_security_button')\n\n        # * A dialog appears; there is an unchecked toggle saying \"Allow JSON API access\"\n        self.wait_for_element_visibility('id=id_security_form', True)\n        self.wait_for_element_visibility('id=id_security_form_save_error', False)\n        self.wait_for_element_visibility('id=id_security_form_json_enabled_checkbox', True)\n        self.wait_for_element_visibility('id=id_security_form_json_api_key', True)\n        self.wait_for_element_visibility('id=id_security_form_json_api_url', True)\n\n        self.assertFalse(self.is_element_enabled('id_security_form_json_api_key'))\n        self.assertFalse(self.is_element_enabled('id_security_form_json_api_url'))\n\n        self.assertEquals(\n            self.get_text('css=label[for=\"id_security_form_json_enabled_checkbox\"]'),\n            'Allow JSON API access'\n        )\n        self.assertEquals(self.selenium.get_value('id=id_security_form_json_enabled_checkbox'), 'off')\n\n        # * ... and OK and Cancel buttons\n        self.wait_for_element_visibility('id=id_security_form_ok_button', True)\n        self.wait_for_element_visibility('id=id_security_form_cancel_button', True)\n\n        # * He checks it.  He notices a textbox giving him an \"API key\",\n        self.selenium.click('id=id_security_form_json_enabled_checkbox')\n        self.assertTrue(self.is_element_enabled('id_security_form_json_api_key'))\n        api_key = self.selenium.get_value('id=id_security_form_json_api_key')\n        api_url = self.selenium.get_value('id=id_security_form_json_api_url')\n\n        # * He also notices that when he clicks on the URL text field, the entire field is selected\n        ## The focus call is to appease Chrome\n        self.selenium.focus('id=id_security_form_json_api_url')\n        self.selenium.click('id=id_security_form_json_api_url')\n\n        # our 'caret' plugin appears to have a problem getting the selection\n        # range for fields that are not editable, such as the json api url.\n        # Consequently, we have to check the selection by copying this\n        # text, and checking the clipboard content.\n        with self.key_down(key_codes.CTRL):\n            self.human_key_press(key_codes.LETTER_C)\n\n        def get_clipboard_text():\n            OpenClipboard()\n            text = GetClipboardData(win32con.CF_TEXT)\n            CloseClipboard()\n            return text\n\n        self.wait_for(\n            lambda: get_clipboard_text() == api_url,\n            lambda: 'bad clipboard text, was: %s' % (get_clipboard_text(),)\n        )\n\n        # * nothing appears outside the JSON API dialog box yet though.\n        self.assertTrue(\n            'JSON API disabled' in\n            self.selenium.get_attribute('css=#id_security_button@title')\n        )\n        self.assertTrue(\n            'JSON API disabled' in\n            self.selenium.get_attribute('css=#id_security_button@alt')\n        )\n\n        # * He ignores all of the key stuff, presses Cancel\n        self.selenium.click('id=id_security_form_cancel_button')\n\n        # * He notices that the form disappears and that the icon still indicates that the JSON API is disabled\n        self.wait_for_element_visibility('id=id_security_form', False)\n        self.assertTrue(\n            'JSON API disabled' in\n            self.selenium.get_attribute('css=#id_security_button@title')\n        )\n        self.assertTrue(\n            'JSON API disabled' in\n            self.selenium.get_attribute('css=#id_security_button@alt')\n        )\n\n        # but he just tries accessing the JSON URL without a key again\n        # * He gets 403 again.\n        with self.assertRaises(HTTPError) as mngr:\n            urlopen(base_json_url)\n        self.assertEquals(mngr.exception.code, 403)\n\n        # * and he also gets 403 when he uses the API Key that was displayed\n        with self.assertRaises(HTTPError) as mngr:\n            urlopen(base_json_url, urlencode({'api_key': api_key}))\n        self.assertEquals(mngr.exception.code, 403)\n\n        # * He half-realises what the problem is, opens the dialog again, checks the box, and presses OK\n        self.selenium.click('id=id_security_button')\n        self.wait_for_element_visibility('id=id_security_form', True)\n        self.wait_for_element_visibility('id=id_security_form_save_error', False)\n        self.assertEquals(self.selenium.get_value('id=id_security_form_json_enabled_checkbox'), 'off')\n        self.selenium.click('id=id_security_form_json_enabled_checkbox')\n        self.assertTrue(self.is_element_enabled('id_security_form_json_api_key'))\n        self.assertTrue(self.is_element_enabled('id_security_form_json_api_url'))\n        api_url = self.selenium.get_value('css=#id_security_form_json_api_url')\n        self.selenium.click('id=id_security_form_ok_button')\n        self.wait_for_element_visibility('id=id_security_form', False)\n\n        #* He now sees the toolbar indicates that the JSON API is enabled for this sheet\n        self.assertTrue(\n            'JSON API enabled' in\n            self.selenium.get_attribute('css=#id_security_button@title')\n        )\n        self.assertTrue(\n            'JSON API enabled' in\n            self.selenium.get_attribute('css=#id_security_button@alt')\n        )\n\n        # * Not trusting the memory of his browser, he opens the dialog again\n        self.selenium.click('id=id_security_button')\n        self.wait_for_element_visibility('id=id_security_form', True)\n        self.wait_for_element_visibility('id=id_security_form_save_error', False)\n        self.assertEquals(self.selenium.get_value('id=id_security_form_json_enabled_checkbox'), 'on')\n\n        # * and immediately presses Cancel\n        self.selenium.click('id=id_security_form_cancel_button')\n\n        # * He is surprised and delighted to see that his sheet is still JSON-enabled\n        self.assertTrue(\n            'JSON API enabled' in\n            self.selenium.get_attribute('css=#id_security_button@title')\n        )\n        self.assertTrue(\n            'JSON API enabled' in\n            self.selenium.get_attribute('css=#id_security_button@alt')\n        )\n\n        expected_url = \"%s%s?api_key=%s\" % (\n            self.selenium.browserURL[:-1],\n            urlparse(Url.api_url(self.get_my_username(), sheet_id)).path,\n            api_key\n        )\n        self.assertEquals(api_url, expected_url)\n\n        # .. despite this helpful link, he tries again with the wrong API key\n        with self.assertRaises(HTTPError) as mngr:\n            urlopen(base_json_url, urlencode({'api_key': 'abcd1234-123dfe'}))\n        # * He gets a 403\n        self.assertEquals(mngr.exception.code, 403)\n\n\n        # * Frustrated, he tries again with the right API key.\n        response = urlopen(base_json_url, urlencode({'api_key': api_key}))\n\n        # * He gets the data he expected.\n        json_data = json.load(response)\n        self.assertEquals(json_data['1']['1'], 5)\n\n        # * He changes the API key in the JSON API dialog.\n        self.selenium.click('id=id_security_button')\n        self.wait_for_element_visibility('id=id_security_form', True)\n        self.wait_for_element_visibility('id=id_security_form_save_error', False)\n        old_api_url = self.selenium.get_value('css=#id_security_form_json_api_url')\n        self.selenium.type('id=id_security_form_json_api_key', 'some_new_api_ke')\n        self.selenium.focus('id=id_security_form_json_api_key')\n\n        # He sees that the api url is updated with every keystroke\n        self.human_key_press(key_codes.END) # Move IE insert point to the end\n        self.human_key_press(key_codes.LETTER_Y)\n\n        self.assertEquals(\n            self.selenium.get_value('css=#id_security_form_json_api_url'),\n            old_api_url.replace(api_key, 'some_new_api_key')\n        )\n        self.selenium.click('id=id_security_form_ok_button')\n        self.wait_for_element_visibility('id=id_security_form', False)\n\n        # * He tries again, using the old key\n        with self.assertRaises(HTTPError) as mngr:\n            urlopen(base_json_url, urlencode({'api_key': api_key}))\n        # * He gets a 403\n        self.assertEquals(mngr.exception.code, 403)\n\n        # * He tries using the right key.\n        response = urlopen(base_json_url, urlencode({'api_key': 'some_new_api_key'}))\n\n        # * It works.\n        json_data = json.load(response)\n        self.assertEquals(json_data['1']['1'], 5)\n\n        # * He refreshes the sheet page\n        self.refresh_sheet_page()\n\n        # * and notes that his setting has been remembered by the server\n        self.selenium.click('id=id_security_button')\n        self.wait_for_element_visibility('id=id_security_form', True)\n        self.wait_for_element_visibility('id=id_security_form_save_error', False)\n        self.assertEquals(self.selenium.get_value('id=id_security_form_json_enabled_checkbox'), 'on')\n\n        # * He makes the sheet private again.\n        self.selenium.click('id=id_security_button')\n        self.wait_for_element_visibility('id=id_security_form', True)\n        self.wait_for_element_visibility('id=id_security_form_save_error', False)\n        self.selenium.click('id=id_security_form_json_enabled_checkbox')\n        self.selenium.click('id=id_security_form_ok_button')\n        self.wait_for_element_visibility('id=id_security_form', False)\n\n        # * He tries with the key that worked last time.\n        with self.assertRaises(HTTPError) as mngr:\n            urlopen(base_json_url, urlencode({'api_key': 'some_new_api_key'}))\n        # * He gets a 403\n        self.assertEquals(mngr.exception.code, 403)\n\n\n    def test_link_to_documentation_in_dialog(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He goes to the security dialog.\n        self.selenium.click('id=id_security_button')\n        self.wait_for_element_visibility('id=id_security_form', True)\n        self.wait_for_element_visibility('id=id_security_form_save_error', False)\n\n        # * He is puzzled by the JSON API option, so he clicks on its help link\n        # * The link goes into a new window\n        ## Selenium gets Very Unhappy with target=_blank, so we just check that it's there and\n        ## then remove it so as not to frighten the poor thing.\n        self.assertEquals(\n            self.selenium.get_attribute('id=id_security_form_json_help@target'),\n            \"_blank\"\n        )\n        self.selenium.get_eval(\"window.$('#id_security_form_json_help').removeAttr('target')\")\n        self.selenium.click(\"id=id_security_form_json_help\")\n        self.selenium.wait_for_page_to_load(PAGE_LOAD_TIMEOUT)\n\n        # containing a documentation page that explains it all\n        title = self.browser.title\n        self.assertTrue(title.startswith('The JSON API'))\n        self.assertTrue(title.endswith('documentation'))\n\n\n    def test_run_worksheet_with_json_disabled_sheets(self):\n        # * Harold logs in to Dirigible and creates a new sheet, with some stuff in it\n        self.login_and_create_new_sheet()\n        rws_sheet_url = self.browser.current_url\n        self.enter_cell_text(1, 1, '5')\n\n        # * He creates another new sheet\n        self.create_new_sheet()\n        base_json_url = urljoin(self.browser.current_url, 'v0.1/json/')\n\n        # * and enables JSON API access\n        self.selenium.click('id=id_security_button')\n        self.wait_for_element_visibility('id=id_security_form', True)\n        self.wait_for_element_visibility('id=id_security_form_save_error', False)\n        self.selenium.click('id=id_security_form_json_enabled_checkbox')\n        self.selenium.type('id=id_security_form_json_api_key', self.get_my_username())\n        self.selenium.click('id=id_security_form_ok_button')\n        self.wait_for_element_visibility('id=id_security_form', False)\n\n        # * He enters a formula that uses run_worksheet on the first sheet\n        self.enter_cell_text(1, 1, \"=run_worksheet('%s')[1, 1].value\" % (rws_sheet_url,))\n        self.wait_for_cell_value(1, 1, '5')\n\n        # * and tries to access it via the JSON API\n        try:\n            response = urlopen(base_json_url, urlencode({'api_key': self.get_my_username()}))\n        except HTTPError, err:\n            self.fail(err.read())\n\n        # * It works.\n        json_data = json.load(response)\n        self.assertEquals(json_data['1']['1'], 5)\n\n\n    def test_json_api_auth_reports_server_error(self):\n        # * Harold logs in to Dirigible and creates a new sheet, with some stuff in it\n        self.login_and_create_new_sheet()\n\n        # * and enables JSON API access\n        self.selenium.click('id=id_security_button')\n        self.wait_for_element_visibility('id=id_security_form', True)\n        self.wait_for_element_visibility('id=id_security_form_save_error', False)\n        self.selenium.click('id=id_security_form_json_enabled_checkbox')\n        self.selenium.type('id=id_security_form_json_api_key', self.get_my_username())\n\n        # Something goes wrong with the Dirigible server\n        old_set_security_settings_url = self.selenium.get_eval(\"window.urls.setSecuritySettings\")\n        self.selenium.get_eval(\"window.urls.setSecuritySettings = 'blergh'\")\n\n        # Blissfully unaware of this, Harold clicks OK.\n        self.selenium.click('id=id_security_form_ok_button')\n\n        # The dialog remains, and an error div appears to tell him that something is wrong.\n        self.wait_for_element_visibility('id=id_security_form', True)\n        self.wait_for_element_visibility('id=id_security_form_save_error', True)\n\n        # He waits for a moment, and the server magically fixes itself.\n        self.selenium.get_eval(\"window.urls.setSecuritySettings = '%s'\" % (old_set_security_settings_url,))\n\n        # He tries again, and the dialog disappears.\n        self.selenium.click('id=id_security_form_ok_button')\n        self.wait_for_element_visibility('id=id_security_form', False)\n\n        # The sheet page confirms that his changes are there.\n        self.assertTrue(\n            'JSON API enabled' in\n            self.selenium.get_attribute('css=#id_security_button@title')\n        )\n        self.assertTrue(\n            'JSON API enabled' in\n            self.selenium.get_attribute('css=#id_security_button@alt')\n        )\n\n        # He pops up the dialog and is pleased to see that there is no error message there.\n        self.selenium.click('id=id_security_button')\n        self.wait_for_element_visibility('id=id_security_form', True)\n        self.wait_for_element_visibility('id=id_security_form_save_error', False)\n\n        # He refreshes the page and confirms from the toolbar buttons that his changes really were saved.\n        self.refresh_sheet_page()\n        self.assertTrue(\n            'JSON API enabled' in\n            self.selenium.get_attribute('css=#id_security_button@title')\n        )\n        self.assertTrue(\n            'JSON API enabled' in\n            self.selenium.get_attribute('css=#id_security_button@alt')\n        )\n"
  },
  {
    "path": "dirigible/fts/tests/test_2571_DocumentationAndBlogLinks.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\r\n# All Rights Reserved\r\n#\r\n\r\nfrom urlparse import urljoin\r\n\r\n\r\nfrom functionaltest import FunctionalTest, Url\r\n\r\n\r\nclass Test_2571_Documentation(FunctionalTest):\r\n\r\n    def get_link_href(self, link_id):\r\n        url = self.selenium.get_attribute(\"id=%s@href\" % (link_id,))\r\n        return urljoin(Url.ROOT, url)\r\n\r\n\r\n    def test_main_documentation_page_exists(self):\r\n        # * Harold goes to the Dirigible front page.\r\n        self.go_to_url(Url.ROOT)\r\n\r\n        # * He sees that there is a link to a blog, and notes down where it goes to.\r\n        blog_url = self.get_link_href(\"id_blog_link\")\r\n\r\n        # * He goes to url http://IP/documentation\r\n        self.go_to_url(Url.DOCUMENTATION)\r\n\r\n        # * He gets back a page, not a 404, the title of which which contains the words 'Dirigible' and 'documentation'\r\n        title = self.browser.title.lower()\r\n        self.assertTrue(\"documentation\" in title)\r\n        self.assertTrue(\"dirigible\" in title)\r\n\r\n        # * He logs in and creates a sheet, and notices that there is a link to the\r\n        #   same documentation page.\r\n        self.login_and_create_new_sheet()\r\n        self.assertEquals(self.get_link_href(\"id_help_link\"), Url.DOCUMENTATION)\r\n\r\n        # ...and that there is also a link to the same blog.\r\n        self.assertEquals(self.get_link_href(\"id_blog_link\"), blog_url)\r\n\r\n        # * He follows the link to his dashboard.\r\n        self.click_link('id_account_link')\r\n\r\n        # * He sees the same help and blog links there, and confirms they go to the same places.\r\n        self.assertEquals(self.get_link_href(\"id_help_link\"), Url.DOCUMENTATION)\r\n        self.assertEquals(self.get_link_href(\"id_blog_link\"), blog_url)"
  },
  {
    "path": "dirigible/fts/tests/test_2577_SaveColumnWidths.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom textwrap import dedent\nimport time\n\nfrom functionaltest import FunctionalTest, PAGE_LOAD_TIMEOUT\n\n\nDELTA = 30\n\nclass Test_2537_save_column_widths(FunctionalTest):\n\n\n    def get_column_header_locator(self, column_name):\n        return 'css=div[title=%s]' % (column_name,)\n\n\n    def get_column_resize_handle_locator(self, column_name):\n        return \"%s div.slick-resizable-handle\" % (self.get_column_header_locator(column_name),)\n\n\n    def get_column_width(self, column_name):\n        return self.selenium.get_element_width(self.get_column_header_locator(column_name))\n\n\n    def wait_for_column_width(self, column_name, width):\n        self.wait_for(\n            lambda: self.get_column_width(column_name) == width,\n            lambda: 'column %s width to become %s (was %s)' % (column_name, width, self.get_column_width(column_name))\n        )\n\n\n    def resize_column(self, column_name, width_delta):\n        orig_column_width = self.get_column_width(column_name)\n\n        self.selenium.drag_and_drop(\n            self.get_column_resize_handle_locator(column_name),\n            '%d,+0' % (width_delta,)\n        )\n\n        self.wait_for_column_width(column_name, orig_column_width + width_delta)\n\n\n\n    def test_save_em(self):\n        # * Harold logs in and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # He resizes a column\n        self.resize_column('B', 30)\n\n        new_size = self.get_column_width('B')\n\n        # * He refreshes the page\n        self.refresh_sheet_page()\n\n        # * the resized column is still enbiggenened\n        self.wait_for_grid_to_appear()\n        self.wait_for_column_width('B', new_size)\n\n\n\n    def test_changing_column_widths_does_not_interrupt_recalc_but_does_save_widths(self):\n        # * Harold logs in and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He sets up a long recalc\n        self.enter_cell_text(1, 1, '1')\n        self.append_usercode(dedent('''\n        worksheet[1, 1].value += 1\n        import time\n        time.sleep(30)\n        '''))\n\n        # * After it's been running for a while, but before it is finished,\n        #   he plays with the column widths.  (Waiting for this makes sure that\n        #   we can distinguish between the completion of the first recalc (which\n        #   should happen within the time the usercode takes to execute plus or\n        #   minus a bit) and the completion of a second recalc that was somehow\n        #   triggered by broken column-resize code; resizing columns should\n        #   *not* trigger a recalc.)\n        time.sleep(20)\n        self.resize_column('B', 30)\n        col_b_width = self.get_column_width('B')\n        self.resize_column('C', -10)\n        col_c_width = self.get_column_width('C')\n\n        # * and notes that the recalc completes normally -- ie.\n        #   that when it completed it did not see the updated\n        #   column widths and think that the sheet had changed\n        #   underneath it, and thus abort and send back an error\n        #   -- and also that it completes within <usercode-time>\n        #   from the initial setting of the usercode, not within\n        #   a larger amount of time (which would imply that setting\n        #   column widths triggered a recalc.\n        self.wait_for_cell_value(1, 1, '2', timeout_seconds=15)\n\n        # * He then refreshes the page to make sure that the recalc\n        #   that just finished copied the new column widths to the DB.\n        self.refresh_sheet_page()\n        self.wait_for_grid_to_appear()\n        self.wait_for_column_width('B', col_b_width)\n        self.wait_for_column_width('C', col_c_width)\n"
  },
  {
    "path": "dirigible/fts/tests/test_2581_FormulaBar.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom functionaltest import FunctionalTest, snapshot_on_error\nimport key_codes\n\n\nclass Test_2581_FormulaBar(FunctionalTest):\n\n    def wait_for_formula_bar_enabled(self):\n        self.wait_for(\n            self.is_formula_bar_enabled,\n            lambda: 'formula bar to enable',\n        )\n\n    def wait_for_formula_bar_disabled(self):\n        self.wait_for(\n            lambda: not self.is_formula_bar_enabled(),\n            lambda: 'formula bar to disable',\n        )\n\n    def test_formula_bar(self):\n        # Harold lacks the mental egg-juggling capacity of a Real Programmer,\n        # and wants to be reminded of relevant state as he examines his\n        # spreadsheet.\n\n        # * He logs in to Dirigible and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He observes that there is a single-line text box above the grid.\n        bar_bounds = self.get_element_bounds(self.get_formula_bar_locator())\n\n        grid_locator = \"id=id_grid\"\n        grid_bounds = self.get_element_bounds(grid_locator)\n\n        self.assertTrue(\n            abs(bar_bounds.left - grid_bounds.left) < 50,\n            \"Grid and formula bar not roughly horizontally aligned\"\n        )\n        self.assertTrue(\n            abs(bar_bounds.width - grid_bounds.width) < 100,\n            \"Grid and formula bar not roughly the same width\"\n        )\n        self.assertTrue(\n            bar_bounds.top + bar_bounds.height < grid_bounds.top,\n            \"Formula bar not above grid\"\n        )\n\n        # * He notes that cell A1 is active, and he can edit the formula bar,\n        #   which is empty\n        ## NB click doesn't work here -- it's for links and buttons only.\n        ## Which makes it strange that it works with cells!\n        self.wait_for_cell_to_become_active(1, 1)\n        self.wait_for_formula_bar_enabled()\n        self.click_formula_bar()\n        self.assertTrue(\n            self.is_element_focused('id=%s' % (self.get_formula_bar_id(),)))\n        self.wait_for_formula_bar_contents(\"\")\n\n        # * As he starts to type \"456\", he observes that the cell editor\n        #   updates with each keystroke.\n        self.human_key_press(key_codes.NUMBER_4)\n        # workaround for defect T2708\n        #self.wait_for_cell_editor_content(\"4\")\n\n        self.human_key_press(key_codes.NUMBER_5)\n        self.wait_for_cell_editor_content(\"45\")\n\n        self.human_key_press(key_codes.NUMBER_6)\n        self.wait_for_cell_editor_content(\"456\")\n\n        # * He then edits cell A2\n        self.open_cell_for_editing(1, 2)\n        self.wait_for_cell_to_enter_edit_mode(1, 2)\n\n        # and notes that the formula bar becomes empty\n        self.wait_for_formula_bar_contents(\"\")\n\n        # while cell A1 still contains \"456\".\n        self.wait_for_cell_value(1, 1, \"456\")\n\n        # * He types \"123\"; as he does so, the contents of the formula bar\n        #   update with each keystroke.\n        self.human_key_press(key_codes.NUMBER_1)\n        self.wait_for_formula_bar_contents(\"1\")\n\n        self.human_key_press(key_codes.NUMBER_2)\n        self.wait_for_formula_bar_contents(\"12\")\n\n        self.human_key_press(key_codes.NUMBER_3)\n        self.wait_for_formula_bar_contents(\"123\")\n\n        # * He edits cell A1, and notes that both the cell and the formula bar\n        #   read \"456\".\n        self.open_cell_for_editing(1, 1)\n        self.wait_for_cell_editor_content(\"456\")\n        self.wait_for_formula_bar_contents(\"456\")\n\n        # * Harold edits A3, and enters \"78\".\n        self.open_cell_for_editing(1, 3)\n        self.human_key_press(key_codes.NUMBER_7)\n        self.human_key_press(key_codes.NUMBER_8)\n\n        #   The formula bar shows 78.\n        self.wait_for_formula_bar_contents(\"78\")\n\n        # * He clicks in the formula bar, hits the \"end\" key, and enters \"90\".\n        self.click_formula_bar()\n        self.human_key_press(key_codes.END)\n        self.human_key_press(key_codes.NUMBER_9)\n        self.human_key_press(key_codes.NUMBER_0)\n\n        #   The cell editor and the formula bar both show 7890.\n        self.wait_for_cell_editor_content(\"7890\")\n        self.assert_formula_bar_contains(\"7890\")\n\n        # * Harold clicks on A4,\n        self.click_on_cell(1, 4)\n\n        #   and then wonders whether he can commit changes from the formula bar\n        #   without click to a cell, so he clicks on the formula bar,\n        self.click_formula_bar()\n\n        #  types \"321<ENTER>\".\n        self.human_key_press(key_codes.NUMBER_3)\n        self.human_key_press(key_codes.NUMBER_2)\n        self.human_key_press(key_codes.NUMBER_1)\n        self.human_key_press(key_codes.ENTER)\n\n        #    and is delighted to see the cell update.\n        self.wait_for_cell_value(1, 4, \"321\")\n\n\n    @snapshot_on_error\n    def test_formula_bar_remains_enabled_at_all_times(self):\n\n        # Harry logs in and creates a new sheet.\n        self.login_and_create_new_sheet()\n\n        # * the formula bar is enabled because a cell has focus\n        self.wait_for_formula_bar_enabled()\n        self.wait_for_cell_to_become_active(1, 1)\n\n        # * he clicks in the output pane\n        #  the formula bar remains enabled\n        self.selenium.click('id=id_console')\n        self.wait_for_formula_bar_enabled()\n\n        # * he clicks in the usercode\n        #  the formula bar remains enabled\n        self.selenium.get_eval('window.editor.focus()')\n        self.wait_for_formula_bar_enabled()\n\n        # * he clicks in the sheet title\n        #  the formula bar remains enabled\n        self.selenium.click('id=id_sheet_name')\n        self.wait_for_formula_bar_enabled()\n\n        # He presses escape to cancel the sheet title edit\n        self.human_key_press(key_codes.ESCAPE)\n\n        #  * He clicks in the formula bar\n        self.click_formula_bar()\n\n        #  and types \"321<ENTER>\".\n        self.human_key_press(key_codes.NUMBER_3)\n        self.human_key_press(key_codes.NUMBER_2)\n        self.human_key_press(key_codes.NUMBER_1)\n        self.human_key_press(key_codes.ENTER)\n\n        # and is delighted to see cell A1 update.\n        self.wait_for_cell_value(1, 1, \"321\")\n"
  },
  {
    "path": "dirigible/fts/tests/test_2582_ReferencingEmptyCell.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\n\nfrom functionaltest import FunctionalTest\n\n\nclass Test_2582_ReferencingEmptyCell(FunctionalTest):\n\n    def test_referencing_empty_cell(self):\n        # Harold logs in and creates a new sheet.\n        self.login_and_create_new_sheet()\n\n        # He enters \"=A1\" into B1\n        self.enter_cell_text(2, 1, '=A1')\n\n        # He sees that B1 is in its \"I have no value but my formula is '=A1'\" state.\n        self.wait_for_cell_shown_formula(2, 1, '=A1')\n\n        # Cell A1 is blank.\n        self.wait_for_cell_value(1, 1, '')\n        self.wait_for_spinner_to_stop()\n\n        # There is nothing in the error console.\n        self.assertTrue(\n            self.get_console_content().startswith('Took'),\n            \"Unexpected error console content:\\n%r\" % (self.get_console_content(),)\n        )\n\n        # He adds code to set the value of C1 to the end of the user code.\n        self.append_usercode(\"worksheet[3, 1].value = 23\")\n\n        # He notes that it is being executed.\n        self.wait_for_cell_value(3, 1, '23')\n"
  },
  {
    "path": "dirigible/fts/tests/test_2592_Cut_Copy_Paste_Within_Dirigible.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom __future__ import with_statement\n\nfrom textwrap import dedent\n\nfrom functionaltest import FunctionalTest, Url\nimport key_codes\n\nclass Test_2592_Cut_Copy_Paste_within_Dirigible(FunctionalTest):\n\n    def wait_for_only_active_cell_selected(self):\n        def is_only_active_cell_selected():\n            return self.selenium.get_eval(\"\"\"\n                (function() {\n                    var selectedCells = window.$('.selected');\n                    if (selectedCells.length !== 1) {\n                        return false;\n                    }\n\n                    return selectedCells[0] === window.$('.active')[0];\n                })()\n            \"\"\") == 'true'\n\n        self.wait_for(\n            is_only_active_cell_selected,\n            lambda: \"Only active cell to be selected\"\n        )\n\n\n    def test_block_selection(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # He tries to select a range by dragging from B2 to C5\n        self.mouse_drag((2, 3), (3, 6))\n        self.assert_current_selection((2, 3), (3, 6))\n\n        # he clicks outside the range and sees his selection disappear\n        self.click_on_cell(7, 7)\n        self.wait_for_only_active_cell_selected()\n\n        # He then uses shift + arrow keys\n\n        # selenium needs a little jquery-kick to get shift-key-down working\n        self.selenium.get_eval(\"window.$('div.grid-canvas').trigger({type:'keydown', which: 16});\")\n\n        self.human_key_press(key_codes.LEFT)\n        self.assert_current_selection((6, 7), (7, 7))\n        self.human_key_press(key_codes.LEFT)\n        self.assert_current_selection((5, 7), (7, 7))\n        self.human_key_press(key_codes.LEFT)\n        self.assert_current_selection((4, 7), (7, 7))\n        self.human_key_press(key_codes.UP)\n        self.assert_current_selection((4, 6), (7, 7))\n        self.human_key_press(key_codes.UP)\n        self.assert_current_selection((4, 5), (7, 7))\n        self.human_key_press(key_codes.UP)\n        self.assert_current_selection((4, 4), (7, 7))\n        self.selenium.get_eval(\"window.$('div.grid-canvas').trigger({type:'keyup', which: 16});\")\n\n        # he clicks *inside* the range and sees his selection disappear\n        self.click_on_cell(5, 5)\n        self.wait_for_only_active_cell_selected()\n\n        # * He finally settles on shift + mouse click as his favourite\n        #   method for selecting a range\n        self.click_on_cell(2, 3)\n        with self.key_down(key_codes.SHIFT):\n            self.click_on_cell(8, 7)\n        self.assert_current_selection((2, 3), (8, 7))\n\n        # he checks you cannot select the header areas\n        self.click_on_cell(1, 1)\n        self.selenium.get_eval(\"window.$('div.grid-canvas').trigger({type:'keydown', which: 16});\")\n        self.human_key_press(key_codes.LEFT)\n        self.assert_current_selection((1, 1), (1, 1))\n        self.human_key_press(key_codes.UP)\n        self.assert_current_selection((1, 1), (1, 1))\n\n\n    def assert_copy_and_paste(\n        self, operation, source_sheet,\n        dest_sheet=None, dest_location=None, to_set='formula'\n    ):\n        if dest_sheet is None:\n            dest_sheet = source_sheet\n\n        # he populates some cells using usercode (because the test runs faster)\n        orig_usercode = self.get_usercode()\n        self.prepend_usercode(dedent('''\n            for row in range(3, 6):\n                for col in 'BC':\n                    worksheet[col, row].%s = '%%s%%d' %% (col, row)\n        ''' % (to_set,)\n        ))\n        self.wait_for_cell_value(2, 3, 'B3')\n\n        if to_set == 'formula':\n            self.enter_usercode(orig_usercode)\n            self.wait_for_spinner_to_stop()\n\n        # he copies (or cuts) a region from one place\n        operation((2, 3), (3, 5))\n\n        # If he's cutting, it disappears\n        if operation == self.cut_range:\n            self.wait_for_cell_value(2, 3, '')\n            self.wait_for_cell_value(3, 3, '')\n            self.wait_for_cell_value(2, 4, '')\n            self.wait_for_cell_value(3, 4, '')\n            self.wait_for_cell_value(2, 5, '')\n            self.wait_for_cell_value(3, 5, '')\n\n        # ...and pastes it elsewhere\n        if dest_sheet != source_sheet:\n            self.go_to_url( Url.sheet_page(self.get_my_username(), dest_sheet) )\n            self.wait_for_grid_to_appear()\n\n        self.paste_range(dest_location)\n\n        # the destination is populated\n        c, r = dest_location\n        self.wait_for_cell_value(  c,   r, 'B3')\n        self.wait_for_cell_value(1+c,   r, 'C3')\n        self.wait_for_cell_value(  c, 1+r, 'B4')\n        self.wait_for_cell_value(1+c, 1+r, 'C4')\n        self.wait_for_cell_value(  c, 2+r, 'B5')\n        self.wait_for_cell_value(1+c, 2+r, 'C5')\n\n\n    def test_copy_and_paste_southeast(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        sheet_id = self.login_and_create_new_sheet()\n\n        # he does a copy and paste, and sees the destination cells update\n        self.assert_copy_and_paste(self.copy_range, sheet_id, dest_location=(3, 4))\n\n        # the non-overlapped source cells are unchanged\n        self.wait_for_cell_value(2, 3, 'B3')\n        self.wait_for_cell_value(2, 4, 'B4')\n        self.wait_for_cell_value(2, 5, 'B5')\n        self.wait_for_cell_value(3, 3, 'C3')\n\n\n    def test_copy_and_paste_northwest(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        sheet_id = self.login_and_create_new_sheet()\n\n        self.assert_copy_and_paste(self.copy_range, sheet_id, dest_location=(1, 2))\n\n        # the non-overlapped source cells are unchanged\n        self.wait_for_cell_value(2, 5, 'B5')\n        self.wait_for_cell_value(3, 3, 'C3')\n        self.wait_for_cell_value(3, 4, 'C4')\n        self.wait_for_cell_value(3, 5, 'C5')\n\n\n    def test_copy_and_paste_to_new_sheet(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        dest_sheet = self.login_and_create_new_sheet()\n        source_sheet = self.create_new_sheet()\n\n        self.assert_copy_and_paste(\n            self.copy_range, source_sheet, dest_sheet, dest_location=(3, 4))\n\n        # the cells in the original sheet are all still there\n        self.go_to_url( Url.sheet_page(self.get_my_username(), source_sheet) )\n        self.wait_for_grid_to_appear()\n        self.wait_for_cell_value(2, 3, 'B3')\n        self.wait_for_cell_value(2, 4, 'B4')\n        self.wait_for_cell_value(2, 5, 'B5')\n        self.wait_for_cell_value(3, 3, 'C3')\n        self.wait_for_cell_value(3, 4, 'C4')\n        self.wait_for_cell_value(3, 5, 'C5')\n\n\n    def test_cut_and_paste_southeast(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        sheet_id = self.login_and_create_new_sheet()\n\n        self.assert_copy_and_paste(self.cut_range, sheet_id, dest_location=(3, 4))\n\n        # the source cells that weren't pasted over are cleared\n        self.wait_for_cell_value(2, 3, '')\n        self.wait_for_cell_value(3, 3, '')\n        self.wait_for_cell_value(2, 4, '')\n        self.wait_for_cell_value(2, 5, '')\n\n\n    def test_cut_and_paste_northwest(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        sheet_id = self.login_and_create_new_sheet()\n\n        self.assert_copy_and_paste(self.cut_range, sheet_id, dest_location=(1, 2))\n\n        # the source cells that weren't pasted over are cleared\n        self.wait_for_cell_value(2, 5, '')\n        self.wait_for_cell_value(3, 3, '')\n        self.wait_for_cell_value(3, 4, '')\n        self.wait_for_cell_value(3, 5, '')\n\n\n    def test_cut_and_paste_to_new_sheet(self):\n        # * Harold logs in to Dirigible and creates two new sheets\n        dest_sheet = self.login_and_create_new_sheet()\n        source_sheet = self.create_new_sheet()\n\n        # He cuts stuff from one to the other\n        self.assert_copy_and_paste(\n            self.cut_range, source_sheet, dest_sheet, dest_location=(3, 4))\n\n\n    def test_multiple_pastes_after_copy(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        sheet_id = self.login_and_create_new_sheet()\n\n        # He copies some stuff towards the southeast\n        self.assert_copy_and_paste(self.copy_range, sheet_id, dest_location=(3, 4))\n\n        # the non-overlapped source cells are unchanged\n        self.wait_for_cell_value(2, 3, 'B3')\n        self.wait_for_cell_value(2, 4, 'B4')\n        self.wait_for_cell_value(2, 5, 'B5')\n        self.wait_for_cell_value(3, 3, 'C3')\n\n        # he then tries pasting again, even further south east.\n        c, r = 10, 20\n        self.paste_range((c, r))\n\n        # the destination is populated\n        self.wait_for_cell_value(  c,   r, 'B3')\n        self.wait_for_cell_value(1+c,   r, 'C3')\n        self.wait_for_cell_value(  c, 1+r, 'B4')\n        self.wait_for_cell_value(1+c, 1+r, 'C4')\n        self.wait_for_cell_value(  c, 2+r, 'B5')\n        self.wait_for_cell_value(1+c, 2+r, 'C5')\n\n        # the source and original paste are unchanged\n        self.wait_for_cell_value(2, 3, 'B3')\n        self.wait_for_cell_value(3, 4, 'B3')\n\n        # he sets a cell formula\n        self.enter_cell_text(1, 1, \"hope this doesn't kill the clipboard!\")\n        self.wait_for_cell_value(1, 1, \"hope this doesn't kill the clipboard!\")\n\n        # he tries to paste again slightly southeast\n        c, r = 11, 21\n        self.paste_range((c, r))\n\n        # and is pleased to see the destination populated\n        self.wait_for_cell_value(  c,   r, 'B3')\n        self.wait_for_cell_value(1+c,   r, 'C3')\n        self.wait_for_cell_value(  c, 1+r, 'B4')\n        self.wait_for_cell_value(1+c, 1+r, 'C4')\n        self.wait_for_cell_value(  c, 2+r, 'B5')\n        self.wait_for_cell_value(1+c, 2+r, 'C5')\n\n        # he double checks the source and earlier pastes are unchanged\n        self.wait_for_cell_value(2, 3, 'B3')\n        self.wait_for_cell_value(3, 4, 'B3')\n        self.wait_for_cell_value(10, 20, 'B3')\n\n\n    def test_multiple_pastes_after_cut(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        sheet_id = self.login_and_create_new_sheet()\n\n        #  He cuts & pastes some stuff towards the southeast\n        self.assert_copy_and_paste(self.cut_range, sheet_id, dest_location=(3, 4))\n\n        # he hits paste again, further down\n        c, r = 10, 20\n        self.paste_range((c, r))\n\n        # the destination is populated\n        self.wait_for_cell_value(  c,   r, 'B3')\n        self.wait_for_cell_value(1+c,   r, 'C3')\n        self.wait_for_cell_value(  c, 1+r, 'B4')\n        self.wait_for_cell_value(1+c, 1+r, 'C4')\n        self.wait_for_cell_value(  c, 2+r, 'B5')\n        self.wait_for_cell_value(1+c, 2+r, 'C5')\n\n        # he now sets a cell formula\n        self.enter_cell_text(1, 1, \"hope this doesn't kill the clipboard!\")\n        self.wait_for_cell_value(1, 1, \"hope this doesn't kill the clipboard!\")\n\n        # he tries to paste again slightly southeast\n        c, r = 11, 21\n        self.paste_range((c, r))\n\n        # and is pleased to see the destination populated\n        self.wait_for_cell_value(  c,   r, 'B3')\n        self.wait_for_cell_value(1+c,   r, 'C3')\n        self.wait_for_cell_value(  c, 1+r, 'B4')\n        self.wait_for_cell_value(1+c, 1+r, 'C4')\n        self.wait_for_cell_value(  c, 2+r, 'B5')\n        self.wait_for_cell_value(1+c, 2+r, 'C5')\n\n        # he double checks the original source is still empty\n        self.wait_for_cell_value(2, 3, '')\n\n        #and earlier pastes are unchanged\n        self.wait_for_cell_value(3, 4, 'B3')\n        self.wait_for_cell_value(10, 20, 'B3')\n\n\n    def test_values_wo_formulae_are_promoted(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        sheet_id = self.login_and_create_new_sheet()\n\n        # he sets up a range with just values, no formulae, and checks they get\n        # copied\n        self.assert_copy_and_paste(self.copy_range, sheet_id,\n            dest_location=(3, 4), to_set='value')\n\n\n    def test_target_range_is_cleared_before_paste(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He enters some values into the grid\n        #   making sure that there are some gaps\n        for row in range(1, 6, 2):\n            self.enter_cell_text(1, row, 'filled')\n        self.copy_range((1, 1), (1, 6))\n\n        # * He copies a section of the values over another section\n        #   and notes that the gaps in the source range are replicated in the target\n        self.paste_range((1, 2))\n        self.wait_for_cell_value(1, 3, '')\n\n\n    def test_mouse_drags_dont_lose_current_cell_edits(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        self.click_on_cell(1, 1)\n        self.human_key_press(key_codes.NUMBER_1)\n        self.human_key_press(key_codes.NUMBER_2)\n\n        self.mouse_drag((2, 3), (3, 6))\n"
  },
  {
    "path": "dirigible/fts/tests/test_2595_Spinner.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\r\n# All Rights Reserved\r\n#\r\n\r\n\r\nfrom functionaltest import FunctionalTest\r\n\r\n\r\nclass Test_2595_Throbber(FunctionalTest):\r\n\r\n    def test_spinner_appears_during_recalcs(self):\r\n        # * Harold likes to know when dirigible is working hard on his calculations\r\n\r\n        # * He logs in and creates a new sheet\r\n        self.login_and_create_new_sheet()\r\n\r\n        # * When the grid has appeared, the spinner might be visible, but it disappears\r\n        #   rapidly as the initial empty recalc completes.\r\n        self.wait_for_spinner_to_stop()\r\n\r\n        # * and enters some hard-working user-code\r\n        self.append_usercode('import time\\ntime.sleep(20)\\nworksheet[1,1].value=\"ready\"')\r\n\r\n        # * He spots the spinner on the page\r\n        self.wait_for(self.is_spinner_visible,\r\n                      lambda : 'spinner not present',\r\n                      timeout_seconds = 5)\r\n\r\n        # * When the recalc is done, he sees the spinner go away\r\n        self.wait_for_cell_value(1, 1, 'ready', timeout_seconds=25)\r\n\r\n        self.assertTrue(self.is_element_present('css=#id_spinner_image.hidden'))\r\n"
  },
  {
    "path": "dirigible/fts/tests/test_2597_CapRecalcTime.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\n\nfrom functionaltest import FunctionalTest\n\n\nclass Test_2597_CapRecalcTime(FunctionalTest):\n\n    def test_recalcs_get_stopped(self):\n        # * Harold doesn't want to waste money on recalculations where he created an\n        #   infinite loop. Dirigible helpfully kills any recalculations that are\n        #   taking too long. The default (set in the database per sheet) is 60sec.\n\n        # * He logs in and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * and enters some ill-advised user-code\n        self.append_usercode('while True: pass\\n\\n#some stuff')\n        fn_call_line = self.get_usercode().split('\\n').index('while True: pass')\n\n        # * He notes that after a minute, there is a message in the console\n        #   telling him that his recalculation timed out.\n        self.wait_for_console_content(\n            'TimeoutException: \\n    User code line 10',\n            timeout_seconds=59\n        )\n\n"
  },
  {
    "path": "dirigible/fts/tests/test_2601_UndefinedShouldBeAvailableToUsercode.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom functionaltest import FunctionalTest\n\n\nclass Test_2601_UndefinedShouldBeAvailableToUsercode(FunctionalTest):\n\n    def test_use_undefined(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He uses undefined in A1.\n        self.enter_cell_text(1, 1, '=A2==undefined')\n        self.wait_for_cell_value(1, 1, 'True')\n        self.assert_cell_has_no_error(1, 1)\n\n        # * he uses undefined in usercode\n        self.append_usercode('worksheet[1,3].value = worksheet[1,4].value==undefined')\n        self.wait_for_cell_value(1, 3, 'True')\n        self.assertTrue(self.get_console_content().startswith('Took'))\n\n        # hooray!\n\n"
  },
  {
    "path": "dirigible/fts/tests/test_2602_SheetPageShouldDisplayBeforeFirstRecalcComplete.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom textwrap import dedent\n\nfrom functionaltest import FunctionalTest, Url\n\n\nclass Test_2602_SheetPageShouldDisplayBeforeFirstRecalcComplete(FunctionalTest):\n\n    def test_sheet_page_displays_fast(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        self.login_and_create_new_sheet()\n        sheet_page = self.browser.current_url\n\n        # * He sets it up so that it takes 30 seconds to recalc.\n        self.enter_cell_text(1, 2, '=123')\n        self.enter_usercode(dedent(\"\"\"\n            import time\n            time.sleep(30.0)\n            load_constants(worksheet)\n            evaluate_formulae(worksheet)\n            worksheet[2,2].value = 'usercode completed'\n        \"\"\"))\n        self.wait_for_cell_value(2, 2, 'usercode completed', timeout_seconds=35)\n        self.wait_for_cell_value(1, 2, '123')\n\n        # * He navigates to a different page.\n        self.go_to_url(Url.ROOT)\n\n        # * He goes back to the sheet page.\n        self.go_to_url(sheet_page)\n\n        # * He notes that the grid takes less than 10 seconds to appear.\n        self.wait_for_grid_to_appear(timeout_seconds=10)\n\n        # * Once the grid is loaded, but before the recalc has completed, it\n        #   displays the results of his formulae and usercode-only values\n        #   even before the recalc is complete\n        self.wait_for_cell_value(1, 2, '123', timeout_seconds=35)\n        self.wait_for_cell_value(2, 2, 'usercode completed')\n"
  },
  {
    "path": "dirigible/fts/tests/test_2603_WorksheetsMayOnlyContainCells.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\nfrom textwrap import dedent\n\nfrom functionaltest import FunctionalTest\n\n\nclass test_2603_WorksheetsMayOnlyContainCells(FunctionalTest):\n\n    def test_setting_worksheet_location_to_non_cell_raises(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He enters usercode which sets a worksheet location to a non-cell\n        self.append_usercode('worksheet[1,1] = 123')\n        error_line = len(self.get_usercode().split('\\n'))\n        expected = dedent('''\n            TypeError: Worksheet locations must be Cell objects\n                User code line %d''' % (error_line,))[1:]\n        self.wait_for_console_content(expected)\n\n        # hooray!\n\n"
  },
  {
    "path": "dirigible/fts/tests/test_2616_RootPageIsDashboard.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom urlparse import urlparse, urlunparse\n\nfrom functionaltest import FunctionalTest\n\n\nclass Test_2616_RootPageIsDashboard(FunctionalTest):\n\n    # The bulk of this was tested by changing the existing functional tests to reflect the new\n    # behaviour; we just have new stuff in this file.\n\n    def test_old_url_redirects(self):\n        # Harold has some old bookmarks to pages that have moved as part of this story.\n        # He is delighted to find they still work.\n        self.assert_redirects('/static/dirigible/about.html', '/about/')\n        self.assert_redirects('/static/dirigible/contact.html', '/contact/')\n        self.assert_redirects('/static/dirigible/pricing.html', '/pricing/')\n        self.assert_redirects('/static/dirigible/privacy.html', '/privacy/')\n        self.assert_redirects('/static/dirigible/terms.html', '/terms/')\n        self.assert_redirects('/static/dirigible/video.html', '/video/')\n"
  },
  {
    "path": "dirigible/fts/tests/test_2621_CanSaveSheetsWithLotsOfFormulae.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom textwrap import dedent\n\nfrom functionaltest import FunctionalTest, snapshot_on_error\n\n\nclass Test_2621_CanSaveSheetsWithLotsOfFormulae(FunctionalTest):\n\n    @snapshot_on_error\n    def test_can_save_lots_of_formulae(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        self.login_and_create_new_sheet()\n        sheet_page = self.browser.current_url\n\n        # * Typing tirelessly, he enters moderately-sized formulae in\n        #   23 columns across 400 rows\n        cell_value = \"a\" * 5\n        self.enter_usercode(dedent(\"\"\"\n            for col in range(1, 45):\n                for row in range(1, 401):\n                    worksheet[col, row].formula = %r\n        \"\"\" % cell_value))\n        self.wait_for_cell_shown_formula(1, 1, cell_value, timeout_seconds=30)\n        self.enter_usercode(dedent(\"\"\"\n            load_constants(worksheet)\n            evaluate_formulae(worksheet)\n        \"\"\"))\n\n        # * He looks at the grid and sees that the data appear to be\n        #   there\n        self.wait_for_cell_value(1, 1, cell_value, timeout_seconds=30)\n        self.wait_for_cell_value(1, 10, cell_value)\n        self.wait_for_cell_value(10, 1, cell_value)\n        self.wait_for_cell_value(10, 10, cell_value)\n        self.wait_for_cell_value(5, 5, cell_value)\n\n        # * He goes back to his dashboard.\n        self.go_to_url(sheet_page)\n\n        # * He returns to the grid, and is pleased to see that the data\n        #   are still there.\n        self.wait_for_cell_value(1, 1, cell_value, timeout_seconds=30)\n        self.wait_for_cell_value(1, 10, cell_value)\n        self.wait_for_cell_value(10, 1, cell_value)\n        self.wait_for_cell_value(10, 10, cell_value)\n        self.wait_for_cell_value(5, 5, cell_value)\n"
  },
  {
    "path": "dirigible/fts/tests/test_2622_CellRanges.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom functionaltest import FunctionalTest, Url\n\nfrom textwrap import dedent\n\n\nclass Test_2622_CellRanges(FunctionalTest):\n\n    def test_can_use_cell_ranges_in_usercode(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * While perusing the documentation, he notices a reference to cell ranges.\n        sheet_url = self.browser.current_url\n        self.go_to_url(Url.API_DOCS)\n        self.wait_for_element_to_appear(\"id=CellRange\")\n        self.wait_for_element_to_appear(\"id=Worksheet.cell_range\")\n\n        # * Fascinated by this leap forward in the API, he creates a cellrange object in usercode\n        self.go_to_url(sheet_url)\n        self.wait_for_grid_to_appear()\n        self.append_usercode(dedent('''\n            worksheet.B3.value = 1\n            worksheet.C3.value = 2\n            worksheet.D3.value = 'outside'\n            worksheet.B4.value = 3\n            #worksheet C4 is blank\n            worksheet.B5.value = 5\n            worksheet.C5.value = 6\n            #worksheet row 6 is blank\n            worksheet.B7.value = 'a random string in the middle'\n            worksheet.C7.value = 10\n\n            my_range_no_blanks = worksheet.cell_range('B3:B5')\n            cells_total = sum(my_range_no_blanks)\n            worksheet.A1.value = cells_total\n\n            my_full_range_syntax1 = worksheet.cell_range('B3','C10')\n            for col, value in enumerate(my_full_range_syntax1):\n                worksheet[col+2, 1].value = value\n\n            my_full_range_syntax2 = worksheet.cell_range((2,3),'C10')\n            for col, cell in enumerate(my_full_range_syntax2.cells):\n                worksheet[col+2, 2].value = cell.value\n        '''))\n\n        self.wait_for_cell_value(1, 1, '9')\n\n        data1 = [1,2,3,'',5,6,'','','a random string in the middle',10]\n        for col, value in enumerate(data1):\n            self.wait_for_cell_value(col+2, 1, str(value))\n\n        for col, value in enumerate(data1):\n            self.wait_for_cell_value(col+2, 2, str(value))\n\n\n    def test_can_use_cell_ranges_in_formulae(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        self.enter_cell_text(1, 1, '1')\n        self.enter_cell_text(1, 2, '2')\n        self.enter_cell_text(1, 3, '3')\n        self.enter_cell_text(2, 1, '=sum(A1:A3)')\n\n        self.wait_for_cell_value(2, 1, '6')\n\n\n    def test_bare_cell_ranges_in_cells_dont_cause_recursive_explosion(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        self.enter_cell_text(1, 1, '1')\n        self.enter_cell_text(1, 2, '2')\n        self.enter_cell_text(1, 3, '3')\n        self.enter_cell_text(2, 1, '=A1:A3')\n\n        self.wait_for_cell_value(2, 1, \"<CellRange A1 to A3 in <Worksheet>>\")\n\n\n    def test_cellrange_dependencies(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * he creates many cell formulae\n        self.prepend_usercode(dedent('''\n            for col in range(2, 12):\n              for row in range(2, 12):\n                worksheet[col, row].formula = '=1'\n            worksheet[1, 1].formula = '=sum(b2:k11)'\n        '''))\n        # * the sum in a1 ought to be calculated after all\n        #   the other cell formulae\n        self.wait_for_cell_value(1, 1, '100')\n\n"
  },
  {
    "path": "dirigible/fts/tests/test_2631_BlogRedirect.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\ntry:\n    import unittest2 as unittest\nexcept ImportError:\n    import unittest\n\nfrom urllib2 import urlopen\nfrom urlparse import urljoin\n\n\nfrom functionaltest import FunctionalTest, PAGE_LOAD_TIMEOUT, Url\n\n\n# This test is a bit of a pain.  Ideally we'd visit the blog URLs in Selenium and\n# check that get_location() returned the correct redirected-to URLs.  The problem\n# is that when Selenium is driving Chrome, and it visits a page on our tested\n# domain that redirects to another domain, Chrome blows up because it suspects\n# some kind of cross-site scripting attack.  This is a specific instance of the\n# more general problem that a Selenium test driving Chrome can access one and only\n# one domain.\n#\n# As a result, we can't use Selenium, so this test doesn't actually run in a\n# browser.\nclass Test_2631_BlogRedirect(unittest.TestCase):\n\n    def test_blog_page_redirect(self):\n        # Harold has a bookmark to our blog, but it dates from a long time ago when\n        # it was hosted at projectdirigible.com/blog.  He visits it.\n        old_blog_url = urljoin(Url.ROOT, '/blog/')\n        opened_page = urlopen(old_blog_url)\n\n        # He finds himself at blog.projectdirigible.com.\n        self.assertEquals(opened_page.geturl(), \"http://blog.projectdirigible.com/\")\n\n        # He goes to a specific post that he bookmarked because he found it interesting\n        opened_page = urlopen(urljoin(old_blog_url, \"?p=196\"))\n\n        # He finds himself on the equivalent page on the new site.\n        self.assertEquals(opened_page.geturl(), \"http://blog.projectdirigible.com/?p=196\")\n\n\n    def test_blog_rss_redirect(self):\n        # Harold subscribed to our blog's RSS feed a long time ago when it was hosted\n        # at projectdirigible.com/blog.  His RSS reader tries to access the feed.\n        old_rss_url = urljoin(Url.ROOT, '/blog/?feed=rss2')\n        opened_page = urlopen(old_rss_url)\n\n        # It winds up getting it from blog.projectdirigible.com.\n        self.assertEquals(opened_page.geturl(), \"http://blog.projectdirigible.com/?feed=rss2\")"
  },
  {
    "path": "dirigible/fts/tests/test_2633_CursorKeysMoveAroundGrid.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom __future__ import with_statement\n\nfrom functionaltest import FunctionalTest\nimport key_codes\n\n\nclass Test_2633_CursorKeysMoveAroundGrid(FunctionalTest):\n\n    def get_cell_editor_cursor_position(self):\n        try:\n            selection_start = int(self.selenium.get_eval(\"window.$('%s').caret().start\" % (self.get_cell_editor_css(),)))\n            selection_end = int(self.selenium.get_eval(\"window.$('%s').caret().end\" % (self.get_cell_editor_css(),)))\n        except ValueError:\n            return None\n        self.assertEquals(selection_start, selection_end, \"Time to refactor?\")\n        return selection_start\n\n\n    def assert_editing_cell_at_position(self, col, row, position):\n        self.assert_cell_is_current_and_is_editing(col, row)\n        self.assertEquals(position, self.get_cell_editor_cursor_position())\n\n\n\n    def test_navigate_with_cursor_keys(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        self.selenium.get_eval('window.$(\".grid-canvas\").focus();')\n\n        # * He clicks on a random cell\n        self.click_on_cell(3, 4)\n\n        #   and it is clear to him that he is not in edit mode\n        #   ie focus is not captured by the input box\n        self.assert_cell_is_current_but_not_editing(3, 4)\n\n        # * He uses the LRUD cursor keys and sees he is navigating around\n        #   the grid\n        self.human_key_press(key_codes.LEFT)\n        self.assert_cell_is_current_but_not_editing(2, 4)\n\n        self.human_key_press(key_codes.UP)\n        self.assert_cell_is_current_but_not_editing(2, 3)\n\n        self.human_key_press(key_codes.RIGHT)\n        self.assert_cell_is_current_but_not_editing(3, 3)\n\n        self.human_key_press(key_codes.DOWN)\n        self.assert_cell_is_current_but_not_editing(3, 4)\n\n        # * He finds that tab and shift-tab also move right and left\n        self.human_key_press(key_codes.TAB)\n        self.assert_cell_is_current_but_not_editing(4, 4)\n\n        with self.key_down(key_codes.SHIFT):\n            self.human_key_press(key_codes.TAB)\n        self.assert_cell_is_current_but_not_editing(3, 4)\n\n        # * He tries to go past the top row and gets blocked\n        self.click_on_cell(5,1)\n        self.human_key_press(key_codes.UP)\n        self.assert_cell_is_current_but_not_editing(5, 1)\n        #   Same for leftmost column\n        self.click_on_cell(1,5)\n        self.human_key_press(key_codes.LEFT)\n        self.assert_cell_is_current_but_not_editing(1, 5)\n\n        # * He presses down to scroll off the bottom of the current screen\n        #   and notices the grid scrolls down with him\n        self.click_on_cell(1,1)\n        MAXROW = 50\n        for expected_row in range(1, MAXROW):\n            self.assert_cell_is_current_but_not_editing(1, expected_row)\n            self.assert_cell_visible(1, expected_row)\n            self.human_key_press(key_codes.DOWN)\n\n        #   he sees it also works when he goes back up again\n        for expected_row in range(MAXROW, 1, -1):\n            self.assert_cell_is_current_but_not_editing(1, expected_row)\n            self.assert_cell_visible(1, expected_row)\n            self.human_key_press(key_codes.UP)\n\n\n        #   He goes to the edge of the screen on the right\n        #   and notices the grid scrolls right with him.\n        self.click_on_cell(1,1)\n        MAXCOL = 20\n        for expected_col in range(1, MAXCOL):\n            self.assert_cell_is_current_but_not_editing(expected_col, 1)\n            self.assert_cell_visible(expected_col, 1)\n            self.human_key_press(key_codes.RIGHT)\n\n        #   he sees it also works when he goes back left again\n        for expected_col in range(MAXCOL, 1, -1):\n            self.assert_cell_is_current_but_not_editing(expected_col, 1)\n            self.assert_cell_visible(expected_col, 1)\n            self.human_key_press(key_codes.LEFT)\n\n        def get_bottom_row():\n            # assumes the grid is scrolled up to row 1 to begin\n            row = 1\n            while True:\n                if not self.is_cell_visible(1, row):\n                    return row\n                row += 1\n\n        # * He clicks in the middle of the visible grid, then\n        #   tries page up, but since the grid is already scrolled\n        #   all the way up, nothing happens\n        self.click_on_cell(5, 5)\n        self.human_key_press(key_codes.PAGE_UP)\n        self.assertEquals(self.get_viewport_top(), 1)\n        self.assert_cell_is_current_but_not_editing(5, 5)\n\n        # * He tries pageDown and notes to his delight\n        #   that the grid scrolls down one page\n        _, old_row = self.get_current_cell()\n        old_distance_from_top = old_row - self.get_viewport_top()\n        old_bottom_row = get_bottom_row()\n        page_size = self.get_viewport_bottom() - self.get_viewport_top() - 1\n        self.human_key_press(key_codes.PAGE_DOWN)\n\n        # correct new current cell is selected:\n        self.assert_cell_is_current_but_not_editing(\n            5, old_row + page_size\n        )\n\n        # grid is scrolled to correct place:\n        _, new_row = self.get_current_cell()\n        new_distance_from_top = new_row - self.get_viewport_top()\n        self.assertEquals(new_distance_from_top, old_distance_from_top)\n\n        # * He uses pageUp to return to the top of the grid\n        _, row = self.get_current_cell()\n        self.human_key_press(key_codes.PAGE_UP)\n        self.assert_cell_is_current_but_not_editing(5, 5)\n\n        # grid is scrolled to correct place:\n        self.assertEquals(self.get_viewport_top(), 1)\n\n        # * He selects a cell right at the bottom of the grid.\n        self.click_on_cell(5, 990)\n\n        # * He hits pageDown a bunch of times and discovers that he\n        #   reaches a stable last row\n        pageDowns = 0\n        last_bottom_row = None\n        hit_bottom = False\n        while pageDowns < 6:\n            current_bottom_row = self.get_viewport_bottom()\n            if current_bottom_row == last_bottom_row:\n                hit_bottom = True\n                break\n            last_bottom_row = current_bottom_row\n            self.human_key_press(key_codes.PAGE_DOWN)\n            pageDowns += 1\n        self.assertTrue(hit_bottom, \"Didn't hit bottom after paging down\")\n\n\n        # * He hits pageUp and notes that the currently-selected cell\n        #   remains in the same place within the visible grid\n        _, old_row = self.get_current_cell()\n        old_distance_from_top = old_row - self.get_viewport_top()\n        old_bottom_row = get_bottom_row()\n        page_size = self.get_viewport_bottom() - self.get_viewport_top() - 1\n        self.human_key_press(key_codes.PAGE_UP)\n\n        _, new_row = self.get_current_cell()\n        new_distance_from_top = new_row - self.get_viewport_top()\n        self.assertEquals(new_distance_from_top, old_distance_from_top)\n\n        # * Amazed at the instinctiveness of Dirigible's grid navigation,\n        #   he wonders whether ctrl+home might take him back to the top left\n        #   and is well pleased when it does\n        with self.key_down(key_codes.CTRL):\n            self.human_key_press(key_codes.HOME)\n        self.assert_cell_is_current_but_not_editing(1, 1)\n        self.assert_cell_visible(1, 1)\n\n        # * Ctrl+end takes him to the bottommost rightest cell also.\n        with self.key_down(key_codes.CTRL):\n            self.human_key_press(key_codes.END)\n        self.assert_cell_is_current_but_not_editing(52, 1000)\n        self.assert_cell_visible(52, 1000)\n\n\n    def test_formula_bar_contents_should_follow_current_cell(self):\n        # Harold logs in and creates a new sheet.\n        self.login_and_create_new_sheet()\n\n        # He enters some data.\n        self.enter_cell_text(1, 1, \"=4567\")\n        self.enter_cell_text(2, 2, \"='B2'\")\n\n        # He moves around the grid using the cursor keys and the mouse and notes\n        # that the contents of the formula bar always reflect the contents of the\n        # selected cell.\n        self.click_on_cell(1, 1)\n        self.wait_for_formula_bar_contents(\"=4567\")\n        self.click_on_cell(1, 2)\n        self.wait_for_formula_bar_contents(\"\")\n        self.click_on_cell(2, 2)\n        self.wait_for_formula_bar_contents(\"='B2'\")\n        self.click_on_cell(2, 1)\n        self.wait_for_formula_bar_contents(\"\")\n\n        self.human_key_press(key_codes.LEFT)\n        self.wait_for_formula_bar_contents(\"=4567\")\n        self.human_key_press(key_codes.DOWN)\n        self.wait_for_formula_bar_contents(\"\")\n        self.human_key_press(key_codes.RIGHT)\n        self.wait_for_formula_bar_contents(\"='B2'\")\n        self.human_key_press(key_codes.UP)\n        self.wait_for_formula_bar_contents(\"\")\n\n        # While an empty cell is selected, he clicks in the formula bar.\n        self.click_on_cell(2, 1)\n        self.wait_for_formula_bar_contents(\"\")\n        self.click_formula_bar()\n\n        # He notes that the cell shifts into edit mode (though it remains empty)\n        full_editor_locator = self.get_cell_editor_locator(2, 1)\n        self.wait_for(\n            lambda : self.is_element_present(full_editor_locator),\n            lambda : \"Editor at (%s, %s) to be present\" % (2, 1),\n        )\n        self.wait_for_cell_editor_content(\"\")\n\n        # He types into the formula bar and hits return.\n        self.human_key_press(key_codes.NUMBER_7)\n\n        # He sees his change applied.\n        # (commented out as workaround for defect T2708)\n        #self.wait_for_cell_value(2, 1, \"7\")\n\n        self.human_key_press(key_codes.NUMBER_8)\n        self.human_key_press(key_codes.ENTER)\n        self.wait_for_cell_value(2, 1, \"78\")\n\n        # He moves over to a populated cell and clicks in the formula bar\n        self.click_on_cell(1, 1)\n        self.wait_for_formula_bar_contents(\"=4567\")\n        self.click_formula_bar()\n\n        # He notes that the cell shifts into edit mode and shows the formula.\n        full_editor_locator = self.get_cell_editor_locator(1, 1)\n        self.wait_for(\n            lambda : self.is_element_present(full_editor_locator),\n            lambda : \"Editor at (%s, %s) to be present\" % (1, 1),\n        )\n        self.wait_for_cell_editor_content(\"=4567\")\n\n        # He changes the contents of the formula bar and hits ENTER.\n        self.human_key_press(key_codes.NUMBER_9)\n        self.human_key_press(key_codes.NUMBER_0)\n        self.human_key_press(key_codes.ENTER)\n\n        # He sees that change applied too.\n        self.wait_for_cell_value(1, 1, \"456790\")\n\n\n    def test_down_and_enter_keys_commit_edit_and_move_current_cell_down(self):\n        # Harold logs in and creates a new sheet.\n        self.login_and_create_new_sheet()\n\n        # He enters some data into cell A1, and hits the down arrow.\n        self.open_cell_for_editing(1, 1)\n        self.human_key_press(key_codes.NUMBER_1)\n        self.human_key_press(key_codes.NUMBER_2)\n        self.human_key_press(key_codes.NUMBER_3)\n        self.human_key_press(key_codes.DOWN)\n\n        # His change is committed into A1\n        self.wait_for_cell_value(1, 1, \"123\")\n\n        # A2 is the current cell.\n        self.assert_cell_is_current_but_not_editing(1, 2)\n\n        # He enters some data into cell B1, and hits the enter key.\n        self.open_cell_for_editing(2, 1)\n        self.human_key_press(key_codes.NUMBER_1)\n        self.human_key_press(key_codes.NUMBER_2)\n        self.human_key_press(key_codes.NUMBER_3)\n        self.human_key_press(key_codes.ENTER)\n\n        # His change is committed into B1\n        self.wait_for_cell_value(2, 1, \"123\")\n\n        # B2 is the current cell.\n        self.assert_cell_is_current_but_not_editing(2, 2)\n\n        # He starts editing C1 in the formula bar and enters some data finishing with an enter.\n        self.click_on_cell(3, 1)\n        self.click_formula_bar()\n        self.human_key_press(key_codes.NUMBER_1)\n        self.human_key_press(key_codes.NUMBER_2)\n        self.human_key_press(key_codes.NUMBER_3)\n        self.human_key_press(key_codes.ENTER)\n\n        # His change is committed into C1\n        self.wait_for_cell_value(3, 1, \"123\")\n\n        # C2 is the current cell.\n        self.assert_cell_is_current_but_not_editing(3, 2)\n\n        # He starts editing D1 in the formula bar and enters some data finishing with an down arrow.\n        self.click_on_cell(4, 1)\n        self.click_formula_bar()\n        self.human_key_press(key_codes.NUMBER_1)\n        self.human_key_press(key_codes.NUMBER_2)\n        self.human_key_press(key_codes.NUMBER_3)\n        self.human_key_press(key_codes.DOWN)\n\n        # His change is *not* committed, and the current cell remains D1.\n        self.wait_for_cell_to_become_active(4, 1)\n        self.assertTrue(self.is_element_focused(self.get_formula_bar_locator()))\n\n\n\n    def test_begin_typing_enters_edit_mode(self):\n        # * Harold logs in and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He clicks on a cell and begins typing\n        self.click_on_cell(2, 3)\n        self.human_key_press(key_codes.NUMBER_1)\n        self.human_key_press(key_codes.NUMBER_2)\n        self.human_key_press(key_codes.NUMBER_3)\n\n        # * He is pleased to see that the cell he clicked on is in edit mode\n        self.wait_for_cell_to_enter_edit_mode(2, 3)\n\n        #   .. and contains the stuff he typed\n        self.wait_for_cell_editor_content('123')\n        #   .. as does the formula bar\n        self.wait_for_formula_bar_contents('123')\n\n        # * He enters some other data into a different cell,\n        #   clicks away from it and then back again\n        self.enter_cell_text(4, 5, '=\"some other data\"')\n        self.click_on_cell(1, 1)\n        self.click_on_cell(4, 5)\n\n        # * He presses some random keys and notes that they don't cause\n        #   the cell to go into edit mode and that the original content\n        #   is unchanged\n        self.human_key_press(key_codes.CTRL)\n        self.assert_cell_is_current_but_not_editing(4, 5)\n\n        self.human_key_press(key_codes.SHIFT)\n        self.assert_cell_is_current_but_not_editing(4, 5)\n\n        # * He clicks on the original cell and presses F2.\n        self.click_on_cell(2, 3)\n\n        # * He types some text and notes that it appears after the original text\n        self.human_key_press(key_codes.F2)\n        self.wait_for_cell_editor_content('123')\n        self.human_key_press(key_codes.NUMBER_5)\n        self.human_key_press(key_codes.NUMBER_1)\n        self.wait_for_cell_editor_content('12351')\n\n        # * He clicks on another cell with content and just starts typing.\n        # * He notes that the previous content is replaced by what he just typed\n        self.click_on_cell(4, 5)\n        self.assert_cell_is_current_but_not_editing(4, 5)\n        self.human_key_press(key_codes.NUMBER_5)\n        self.wait_for_cell_to_enter_edit_mode(4, 5)\n        self.wait_for_cell_editor_content('5')\n\n\n    def test_cursor_keys_while_editing(self):\n        # Harold logs in and creates a new sheet.\n        self.login_and_create_new_sheet()\n\n        # He enters some data into cell B2\n        test_cell = 2, 2\n        self.open_cell_for_editing(*test_cell)\n        self.human_key_press(key_codes.NUMBER_1)\n        self.human_key_press(key_codes.NUMBER_2)\n        self.human_key_press(key_codes.NUMBER_3)\n\n        # He hits the left arrow, and the cursor moves around within his entered data\n        for expected_cursor_position in (2, 1, 0):\n            self.human_key_press(key_codes.LEFT)\n            self.assert_editing_cell_at_position(test_cell[0], test_cell[1], expected_cursor_position)\n\n        # When he tries to move off the left-hand side of the input field, he is stopped.\n        self.human_key_press(key_codes.LEFT)\n        self.assert_editing_cell_at_position(test_cell[0], test_cell[1], 0)\n\n        # He hits the right arrow, and the cursor moves around within his entered data\n        for expected_cursor_position in (1, 2, 3):\n            self.human_key_press(key_codes.RIGHT)\n            self.assert_editing_cell_at_position(test_cell[0], test_cell[1], expected_cursor_position)\n\n        # When he tries to move off the right-hand side of the input field, he is stopped.\n        self.human_key_press(key_codes.RIGHT)\n        self.assert_editing_cell_at_position(test_cell[0], test_cell[1], 3)\n\n        # He hits the up arrow, and sees his changes committed and the current cell move up.\n        self.human_key_press(key_codes.UP)\n        self.wait_for_cell_value(2, 2, \"123\")\n        self.assert_cell_is_current_but_not_editing(2, 1)\n\n        # He goes back into edit mode on cell B2, enters some data, then hits the down\n        # arrow.  His changes are committed and the current cell moves down.\n        self.open_cell_for_editing(*test_cell)\n        self.human_key_press(key_codes.NUMBER_4)\n        self.human_key_press(key_codes.DOWN)\n        self.wait_for_cell_value(2, 2, \"1234\")\n        self.assert_cell_is_current_but_not_editing(2, 3)\n\n\n    def test_editor_font_size_is_consistent(self):\n        # Harold logs in and creates a new sheet.\n        self.login_and_create_new_sheet()\n\n        # * He enters text into a cell\n        self.enter_cell_text(1, 1, \"foo\")\n\n        # * He gets out a ruler and measures the text height\n        cell_font_size_expression = \"window.$('%s').css('font-size')\" % (self.get_cell_css(1, 1),)\n        cell_font_size = self.selenium.get_eval(cell_font_size_expression)\n\n        # * He edits the cell again, and measures the height again while still editing\n        self.open_cell_for_editing(1, 1)\n        editor_font_size_expression = \"window.$('%s').css('font-size')\" % (self.get_cell_editor_css(),)\n        editor_font_size = self.selenium.get_eval(editor_font_size_expression)\n\n        # * He is pleased to discover the two measured heights are the same.\n        self.assertTrue(cell_font_size.endswith('px'))\n        self.assertEquals(cell_font_size, editor_font_size)\n\n\n    def test_enter_single_character(self):\n        ## This is to test for regression of a particular re-occuring bug that\n        ## leads to single characters not committing properly.\n\n        # Harold logs in and creates a new sheet.\n        self.login_and_create_new_sheet()\n\n        # He clicks on cell A1\n        self.click_on_cell(1, 1)\n\n        # He types \"1\"\n        self.human_key_press(key_codes.NUMBER_1)\n\n        # He hits <ENTER>\n        self.human_key_press(key_codes.ENTER)\n\n        # The number \"1\" commits properly and appears in the cell.\n        self.wait_for_cell_value(1, 1, \"1\")\n"
  },
  {
    "path": "dirigible/fts/tests/test_2635_SheetNameSelectedOnEdit.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\ntry:\n    import unittest2 as unittest\nexcept ImportError:\n    import unittest\n\nfrom functionaltest import FunctionalTest\nimport key_codes\n\n\nclass Test_2635_SheetNameSelectedOnEdit(FunctionalTest):\n\n    def test_click_on_sheet_name(self):\n        # * Harold logs in and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He clicks on the sheet name and enters a new one\n        self.selenium.click('id=id_sheet_name')\n        self.wait_for(\n            lambda: self.is_element_present('id=edit-id_sheet_name'),\n            lambda: 'editable sheetname to appear')\n        self.human_key_press(key_codes.LETTER_A)\n        self.human_key_press(key_codes.LETTER_B)\n        self.wait_for(\n            lambda: self.selenium.get_value('id=edit-id_sheet_name') == 'ab',\n            lambda: 'sheetname to change')\n\n"
  },
  {
    "path": "dirigible/fts/tests/test_2639_SciPy_and_MpMath.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom functionaltest import FunctionalTest\n\n\nclass Test_2639_SciPy_and_MpMath(FunctionalTest):\n\n    def test_can_use_scipy_norm(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # He imports scipy\n        self.prepend_usercode(\"from scipy.stats import norm\")\n\n        # and accesses the cumulative normal distribution)\n        # function from a number of cells, and gets the expected results\n        self.enter_cell_text(1, 1, '=norm.cdf(-1000)')\n        self.wait_for_cell_value(1, 1, '0.0')\n\n        self.enter_cell_text(1, 2, '=norm.cdf(0)')\n        self.wait_for_cell_value(1, 2, '0.5')\n\n        self.enter_cell_text(1, 3, '=norm.cdf(1000)')\n        self.wait_for_cell_value(1, 3, '1.0')\n\n\n    def test_can_use_mpmath(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # He imports mpmath\n        self.prepend_usercode(\"import mpmath\")\n\n        # and accesses the mpmath sin function\n        self.enter_cell_text(2, 2, '=mpmath.libmp.BACKEND')\n        self.enter_cell_text(1, 1, '=mpmath.sin(1)')\n        self.wait_for_cell_value(1, 1, \"0.841470984807897\")\n        self.wait_for_cell_value(2, 2, \"gmpy\")\n\n"
  },
  {
    "path": "dirigible/fts/tests/test_2642_RecalcTimesInConsole.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\ntry:\n    import unittest2 as unittest\nexcept ImportError:\n    import unittest\n\nimport re\nfrom functionaltest import FunctionalTest\n\nclass Test_2642_RecalcTimesInConsole(FunctionalTest):\n\n    def test_recalc_time_appears_in_console(self):\n        # * Harold logs in and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He enters some usercode\n        self.append_usercode('worksheet[1, 1].value = 10')\n\n        # * He notes that the console tells him how long the recalc took\n        self.wait_for_cell_value(1, 1, '10')\n        output = self.get_console_content()\n        recalc_time_report_re = re.compile('Took \\d+\\.\\d\\d+s')\n        found_time_report = recalc_time_report_re.search(output)\n        self.assertTrue(found_time_report)\n"
  },
  {
    "path": "dirigible/fts/tests/test_2644_AdminOmniscience.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom functionaltest import FunctionalTest\n\n\nclass Test_2644_AdminOmniscience(FunctionalTest):\n    user_count = 2\n\n    def test_staff_can_see_everything(self):\n        # Harold logs in to Dirigible and creates a new sheet and puts some\n        # super-sekrit data in it.\n        self.login_and_create_new_sheet()\n        self.enter_cell_text(1, 1, \"My banking password\")\n        harolds_sheet_url = self.browser.current_url\n        self.wait_for_cell_value(1, 1, 'My banking password')\n\n        # He logs out, confident that it's safe from prying eyes.\n        self.logout()\n\n        # A member of Dirigible staff logs in, tries to view his sheet and\n        # sees it.\n        self.login(username='admin', password='admin password?')\n        self.go_to_url(harolds_sheet_url)\n        self.wait_for_grid_to_appear()\n        self.wait_for_cell_value(1, 1, \"My banking password\")\n\n\n    def test_non_staff_cannot_view_or_edit_other_users_sheets_or_json(self):\n        harriet = self.get_my_usernames()[1]\n        harold = self.get_my_username()\n\n        # Harriet creates a sheet containing her private data.\n        self.login_and_create_new_sheet(username=harriet)\n        harriets_sheet_url = self.browser.current_url\n        self.logout()\n\n        # Before logging in, Harold tries to access Harriet's sheet using the\n        # correct direct URL, with *her* username and the correct sheet ID.\n        # He gets redirected to the login page\n        self.assert_sends_to_login_page(harriets_sheet_url)\n        self.assert_sends_to_login_page('%scalculate/' % (harriets_sheet_url,))\n        self.assert_sends_to_login_page(\n            '%sset_cell_formula/' % (harriets_sheet_url,))\n        self.assert_sends_to_login_page(\n            '%sget_json_grid_data_for_ui/' % (harriets_sheet_url,))\n        self.assert_sends_to_login_page(\n            '%sget_json_meta_data_for_ui/' % (harriets_sheet_url,))\n\n        # After logging in, Harold tries to access the same sheet\n        # using the correct direct URL, with *her* username and the\n        # correct sheet ID.\n        # He gets a 403 (Access denied) error\n        self.login(username=harold)\n        self.assert_HTTP_error(harriets_sheet_url, 403)\n        self.assert_HTTP_error('%scalculate/' % (harriets_sheet_url,), 403)\n        self.assert_HTTP_error(\n            '%sget_json_grid_data_for_ui/' % (harriets_sheet_url,), 403)\n        self.assert_HTTP_error(\n            '%sget_json_meta_data_for_ui/' % (harriets_sheet_url,), 403)\n        self.assert_HTTP_error(\n            '%sset_cell_formula/' % (harriets_sheet_url,), 403)\n\n"
  },
  {
    "path": "dirigible/fts/tests/test_2650_UsercodeSandbox.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\nfrom textwrap import dedent\n\ntry:\n    import unittest2 as unittest\nexcept ImportError:\n    import unittest\n\nfrom functionaltest import FunctionalTest\n\n\nclass Test_2650_UsercodeSandbox(FunctionalTest):\n\n    def test_dirigible_package_is_off_limits(self):\n        # * Harold logs in and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He puts on his black hat and tries to access the sheets that\n        #   belong to other users\n        self.prepend_usercode('from dirigible.sheet.models import User')\n\n        # * Dirigible notices his naughtiness and stops it\n        self.wait_for_console_content(dedent('''\n            ImportError: No module named models\n                User code line 1''')[1:])\n\n\n    def test_dirigible_settings_not_accessible(self):\n        # * Harold logs in and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He puts on his black hat and tries to access dirigible settings\n        self.prepend_usercode('import dirigible.settings')\n\n        # * Dirigible notices his naughtiness and stops it\n        self.wait_for_console_content(dedent('''\n            ImportError: No module named settings\n                User code line 1''')[1:])\n\n\n    def test_sys_path_omits_dirigible_dirs(self):\n        # * Harold logs in and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He notes that sys.path does not contain any dirigible directories\n        self.append_usercode(dedent('''\n            import sys\n            worksheet[1, 1].value = False\n            for path in sys.path:\n                if 'dirigible' in path:\n                    worksheet[1, 1].value = True\n            worksheet[1, 2].value = 'done'\n        '''))\n\n        self.wait_for_cell_value(1, 2, 'done')\n        self.assertEquals(\n            self.get_cell_text(1, 1), 'False',\n            'there was a dirigible directory in sys.path')\n\n\n    def test_harold_looks_at_his_root_directory(self):\n        # * Harold logs in and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He sees a limited subset of the filesystem\n        self.append_usercode(dedent('''\n            import os\n            worksheet[1, 1].value = sorted(os.listdir(\"/\"))\n        ''')[1:])\n        self.wait_for_cell_value(1, 1, \"['dev', 'etc', 'lib', 'usr']\")\n\n\n    def test_harold_tries_to_create_a_file_in_cwd(self):\n        # * Harold logs in and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He tries to create a file in the current dir\n        self.prepend_usercode(dedent('''\n            import os\n            os.mknod('foo.txt')\n        ''')[1:])\n\n        # * Dirigible notices his naughtiness and stops it\n        self.wait_for_console_content(dedent('''\n            OSError: [Errno 13] Permission denied\n                User code line 2''')[1:])\n\n\n    def test_harold_tries_to_create_a_file_in_usr(self):\n        # * Harold logs in and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He tries to create a file in the current dir\n        self.prepend_usercode(dedent('''\n            import os\n            os.mknod('/usr/lib/foo.txt')\n        ''')[1:])\n\n        # * Dirigible notices his naughtiness and stops it\n        self.wait_for_console_content(dedent('''\n            OSError: [Errno 13] Permission denied\n                User code line 2''')[1:])\n\n"
  },
  {
    "path": "dirigible/fts/tests/test_2651_SaveSheetNameOnBlur.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\ntry:\n    import unittest2 as unittest\nexcept ImportError:\n    import unittest\n\nfrom functionaltest import FunctionalTest\nfrom key_codes import ESCAPE\n\nclass Test_2651_SaveSheetNameOnBlur(FunctionalTest):\n\n    def test_blur_saves_sheet_name(self):\n        # * Harold logs in and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He clicks on the sheet name and enters a new one\n        self.selenium.click('id=id_sheet_name')\n        self.wait_for(\n            lambda: self.is_element_present('id=edit-id_sheet_name'),\n            lambda: 'editable sheetname to appear')\n        self.selenium.type('id=edit-id_sheet_name', 'clicky namey')\n\n        # * He clicks away from the sheet name\n        self.selenium.fire_event('id=edit-id_sheet_name', 'blur')\n\n        # * and notes that his sheet now has the new name\n        self.wait_for(\n            lambda: self.get_text('id=id_sheet_name') == 'clicky namey',\n            lambda: 'sheet name to be updated was \"%s\"' % (self.get_text('id=id_sheet_name'),)\n        )\n\n        # * and the sheet name is not in edit mode\n        self.assertFalse(self.is_element_present('id=edit-id_sheet_name'))\n\n\n    def test_cancel_instruction_present(self):\n        # * Harold logs in and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He clicks on the sheet name and enters a new one\n        original_name = self.get_text('id=id_sheet_name')\n        self.selenium.click('id=id_sheet_name')\n        self.wait_for(\n            lambda: self.is_element_present('id=edit-id_sheet_name'),\n            lambda: 'editable sheetname to appear')\n\n        # * He enters a new name for the sheet\n        self.selenium.type('id=edit-id_sheet_name', \"don't really want this\")\n\n        # * but then decides that he doesn't want that name, so he hits ESC.\n        self.human_key_press(ESCAPE)\n\n        # * The sheet name reverts to the original\n        self.assertEquals(self.get_text('id=id_sheet_name'), original_name)\n"
  },
  {
    "path": "dirigible/fts/tests/test_2652_CommitCellOnBlur.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\ntry:\n    import unittest2 as unittest\nexcept ImportError:\n    import unittest\n\nfrom functionaltest import FunctionalTest\nimport key_codes\n\nclass Test_2652_CommitCellOnBlur(FunctionalTest):\n\n    def test_blur_cell_saves_cell_contents(self):\n        # * Harold logs in and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He edits a cell and types, but doesn't press enter\n        self.open_cell_for_editing(1, 2)\n        self.type_into_cell_editor_unhumanized(\"='hello'\")\n\n        # * He clicks on another cell\n        self.click_on_cell(2, 3)\n        # * and notes that his edit has been commited\n        self.wait_for_cell_value(1, 2, \"hello\")\n\n        # * He types into the new cell, again without pressing enter\n        self.open_cell_for_editing(2, 3)\n        self.type_into_cell_editor_unhumanized(\"='goodbye'\")\n\n        # * and clicks away to something that isn't a cell.\n        self.selenium.click('id=id_console')\n        # * the second edit is also committed\n        self.wait_for_cell_value(2, 3, \"goodbye\")\n\n\n    def test_blur_formula_bar_saves_cell_contents(self):\n        # * Harold logs in and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He double-clicks on a cell\n        self.open_cell_for_editing(1, 2)\n\n        # and types into the formula bar, but doesn't press enter\n        self.click_formula_bar()\n        self.human_key_press(key_codes.NUMBER_1)\n        self.human_key_press(key_codes.NUMBER_2)\n        self.human_key_press(key_codes.NUMBER_3)\n\n        # * He clicks on another cell\n        self.click_on_cell(2, 3)\n        # * and notes that his edit has been commited\n        self.wait_for_cell_value(1, 2, \"123\")\n\n        # * He edits the new cell, again using the formula bar and without pressing enter\n        self.open_cell_for_editing(2, 3)\n        self.click_formula_bar()\n        self.human_key_press(key_codes.NUMBER_4)\n        self.human_key_press(key_codes.NUMBER_5)\n        self.human_key_press(key_codes.NUMBER_6)\n\n        # * and clicks away\n        self.selenium.click('id=id_console')\n\n        # * the second edit is also committed\n        self.wait_for_cell_value(2, 3, \"456\")\n"
  },
  {
    "path": "dirigible/fts/tests/test_2653_UsernameFocusedOnLoginPage.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\n# Check added to T2525\n\n"
  },
  {
    "path": "dirigible/fts/tests/test_2654_CtrlSSavesUsercode.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom __future__ import with_statement\n\nfrom functionaltest import FunctionalTest\nimport key_codes\n\nclass Test_2654_CtrlSSavesUsercode(FunctionalTest):\n\n    def test_ctrl_s_saves_usercode(self):\n        # * Harold logs in and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He enters some usercode\n        self.selenium.get_eval('window.editor.focus()')\n        self.enter_usercode('worksheet[1, 1].value = 5', commit_change=False)\n\n        # * and presses Ctrl-S\n        with self.key_down(key_codes.CTRL):\n            self.human_key_press(key_codes.LETTER_S)\n\n        # * His usercode gets saved and a recalculation starts\n        #self.wait_for_cell_value(1, 1, '5')\n\n        # He types a few more characters, which should appear in the usercode\n        # box, because the save-as dialog didn't appear to capture them\n        self.human_key_press(key_codes.LETTER_A)\n        self.human_key_press(key_codes.LETTER_A)\n        self.human_key_press(key_codes.LETTER_A)\n\n        ## NB enter_usercode leaves the cursor at the end of the editor.\n        self.wait_for_usercode_editor_content(\n            'worksheet[1, 1].value = 5aaa'\n        )\n\n\n"
  },
  {
    "path": "dirigible/fts/tests/test_2678_GlobalStateNotShared.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom functionaltest import FunctionalTest, snapshot_on_error, Url\n\nfrom textwrap import dedent\n\nclass Test_2678_GlobalStateNotShared(FunctionalTest):\n\n    @snapshot_on_error\n    def test_global_state_not_shared(self):\n        # * Harold logs in to Dirigible\n        self.login()\n\n        # * Switching with inhuman speed between several browser windows,\n        #   in each he creates a new sheet with a unique identifier and\n        #   user code that adds the identifier to sys.path\n        try:\n            number_of_windows = 5\n\n            window_ids = []\n            for sheet_number in range(number_of_windows):\n                window_id = \"sheet_window_%s\" % (sheet_number,)\n                self.selenium.open_window(Url.NEW_SHEET, window_id)\n                self.selenium.wait_for_pop_up(window_id, timeout=20000)\n                self.selenium.select_window(window_id)\n                self.wait_for_grid_to_appear()\n                self.append_usercode(dedent('''\n                    import sys\n                    sys.path.append(%r)\n                    worksheet[2, 1].value = %r\n                    worksheet[1, 1].value = sys.path''' % (window_id, window_id)))\n                window_ids.append(window_id)\n\n            # He ensures that they all finish the recalc\n            for window_id in window_ids:\n                self.selenium.select_window(window_id)\n                self.wait_for_cell_value(2, 1, window_id, timeout_seconds=30)\n                self.wait_for_console_content('')\n                sys_path_str = self.get_cell_text(1, 1)\n                self.assertTrue(window_id in sys_path_str)\n                for w_id in window_ids:\n                    if w_id == window_id: continue\n                    if w_id in sys_path_str:\n                        self.fail('Window %r had the sys.path from window %r: %r' % (window_id, w_id, sys_path_str))\n        finally:\n            # Back to our main window\n            #self.selenium.select_window(0)\n            pass\n"
  },
  {
    "path": "dirigible/fts/tests/test_2682_CellAccessUsingA1.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom textwrap import dedent\n\nfrom functionaltest import FunctionalTest\n\n\nclass Test_2682_CellAccessUsingA1(FunctionalTest):\n\n    def test_cell_access(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * he uses some convenient ways to find cells in a worksheet object\n        self.enter_usercode(dedent('''\n            worksheet[1, 2].value = 'a2'\n            worksheet[\"B\", 4].value = 'b4'\n            worksheet[\"C6\"].value = 'c6'\n            worksheet.D8.value = 'd8'\n        '''))\n        # * which seems to work as he expects\n        self.wait_for_cell_value(1, 2, 'a2')\n        self.wait_for_cell_value(2, 4, 'b4')\n        self.wait_for_cell_value(3, 6, 'c6')\n        self.wait_for_cell_value(4, 8, 'd8')\n\n"
  },
  {
    "path": "dirigible/fts/tests/test_2685_ChangePassword.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\nimport time\n\nfrom functionaltest import FunctionalTest, USER_PASSWORD\n\n\nclass Test_2685_ChangePassword(FunctionalTest):\n\n    def test_change_password(self):\n        # * Harold logs in to Dirigible.\n        self.login()\n\n        # He decides to change his password.  He clicks on an appropriate-looking button\n        # in his dashboard.\n        self.assertFalse(self.selenium.is_visible(\"id=id_change_password_form\"))\n        self.selenium.click('id=id_change_password_button')\n\n        # He is presented with a form requiring him to enter his current password once\n        # and the new password twice and OK and Cancel buttons.\n        self.wait_for_element_visibility(\"id=id_change_password_form\", True)\n        self.wait_for_element_visibility(\"id=id_change_password_current_password\", True)\n        self.wait_for_element_visibility(\"id=id_change_password_new_password\", True)\n        self.wait_for_element_visibility(\"id=id_change_password_new_password_again\", True)\n        self.wait_for_element_visibility(\"id=id_change_password_ok_button\", True)\n        self.wait_for_element_visibility(\"id=id_change_password_cancel_button\", True)\n\n        # He enters nothing and submits\n        self.selenium.click('id=id_change_password_ok_button')\n\n        # He gets an appropriate error.\n        self.wait_for_element_text('id=id_change_password_error',\n            'Current password incorrect.')\n\n        # He enters the wrong current password and new passwords that are the same.\n        self.selenium.type('id=id_change_password_current_password', 'random incorrect password')\n        self.selenium.type('id=id_change_password_new_password', 'newpassword')\n        self.selenium.type('id=id_change_password_new_password_again', 'newpassword')\n        self.selenium.click('id=id_change_password_ok_button')\n\n        # He gets an appropriate error.\n        self.wait_for_element_text('id=id_change_password_error',\n            'Current password incorrect.')\n\n        # He enters the right current password and two different new passwords\n        self.selenium.type('id=id_change_password_current_password', USER_PASSWORD)\n        self.selenium.type('id=id_change_password_new_password', 'newpassword')\n        self.selenium.type('id=id_change_password_new_password_again', 'newpassword different')\n        self.selenium.click('id=id_change_password_ok_button')\n\n        # He gets an appropriate error.\n        self.wait_for_element_text('id=id_change_password_error',\n            'Please provide the new password twice for confirmation.')\n\n        # He gives up in frustration and hits a cancel button on the form.\n        self.selenium.click('id=id_change_password_cancel_button')\n\n        # The password change form disappears\n        self.wait_for(\n            lambda: not self.selenium.is_visible(\"id=id_change_password_form\"),\n            lambda: \"password change form to hide\"\n        )\n\n        # and the button re-appears\n        self.wait_for_element_visibility('id=id_change_password_button', True)\n\n        # He opens the form again and notes that his old error message is gone and the password fields have been reset\n        self.selenium.click('id=id_change_password_button')\n        self.wait_for_element_text('id=id_change_password_error', '')\n        self.assertEquals(self.selenium.get_value('id=id_change_password_current_password'), '')\n        self.assertEquals(self.selenium.get_value('id=id_change_password_new_password'), '')\n        self.assertEquals(self.selenium.get_value('id=id_change_password_new_password_again'), '')\n\n        # He logs out.\n        self.logout()\n\n        # After tea and consolation from a sympathetic Harriet, he decides to try again.\n        # He logs in using his original password.\n        self.login()\n\n        # He clicks the change password button.\n        self.selenium.click('id=id_change_password_button')\n\n        # This time he manages to get his old password right and enters the\n        # same new password both times.\n        self.selenium.type('id=id_change_password_current_password', USER_PASSWORD)\n        self.selenium.type('id=id_change_password_new_password', 'newpassword')\n        self.selenium.type('id=id_change_password_new_password_again', 'newpassword')\n        self.selenium.click('id=id_change_password_ok_button')\n\n        # After a few seconds, the form goes away\n        self.wait_for(\n            lambda: not self.selenium.is_visible(\"id=id_change_password_form\"),\n            lambda: \"password change form to hide\"\n        )\n\n        # He is told \"thank you\".\n        self.wait_for_element_visibility(\"id=id_change_password_success\", True)\n        self.wait_for_element_text('id=id_change_password_success',\n            'Your password has been changed.')\n\n        # then after a couple of seconds, the thank-you goes away\n        self.wait_for(\n            lambda: not self.selenium.is_visible(\"id=id_change_password_success\"),\n            lambda: \"password change success notice to hide\",\n            timeout_seconds=6\n        )\n\n        # He logs out\n        self.logout()\n\n        # He tries to log in with the old password.\n        self.login()\n\n        # It doesn't work.\n        self.wait_for_element_text(\"id=id_login_error\",\n            \"The user name or password is incorrect. Please try again.\")\n\n        # He tries the new password.\n        self.login(password='newpassword')\n\n        # It works.\n        expected = \"%s's Dashboard: Dirigible\" % (self.get_my_username(),)\n        self.assertEquals(self.browser.title, expected)\n\n        # He celebrates.\n\n"
  },
  {
    "path": "dirigible/fts/tests/test_2689_DontClearCellEditorWhenRecalcsHitClient.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom textwrap import dedent\nfrom time import sleep\n\nfrom functionaltest import FunctionalTest\nimport key_codes\n\n\nclass Test_2689_DontClearCellEditorOrFormulaBarWhenRecalcsHitClient(FunctionalTest):\n\n    def test_cell_editor_not_cleared(self):\n        # * Harold logs in and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He creates a sheet that will take at least 5 seconds to recalc.\n        self.append_usercode(dedent(\"\"\"\n            import time\n            time.sleep(25)\n        \"\"\"))\n\n        # * He waits for the first recalc to complete\n        self.wait_for_spinner_to_stop(timeout_seconds=30)\n\n        # * He enters 1 in cell A1\n        self.enter_cell_text(1, 1, \"1\")\n\n        # * Without waiting for the recalc to complete, he edits cell A2 and types \"test\" but doesn't hit return.\n        self.open_cell_for_editing(1, 2)\n        self.wait_for_cell_to_enter_edit_mode(1, 2)\n        self.type_into_cell_editor_unhumanized(\"test\")\n\n        # * He waits for more than 25 seconds, and notes that the recalc ends.\n        sleep(25)\n        self.wait_for_spinner_to_stop(timeout_seconds=20)\n\n        # * The cell editor still contains \"test\"\n        self.wait_for_cell_editor_content(\"test\")\n\n\n    def test_formula_bar_not_cleared(self):\n        # * Harold logs in and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He creates a sheet that will take at least 5 seconds to recalc.\n        self.append_usercode(dedent(\"\"\"\n            import time\n            time.sleep(25)\n        \"\"\"))\n\n        # * He waits for the first recalc to complete\n        self.wait_for_spinner_to_stop(timeout_seconds=30)\n\n        # * He enters 1 in cell A1\n        self.enter_cell_text(1, 1, \"1\")\n\n        # * Without waiting for the recalc to complete, he edits cell A2 and types \"123\" into the formula\n        #   bar, but doesn't hit return.\n        self.open_cell_for_editing(1, 2)\n        self.wait_for_cell_to_enter_edit_mode(1, 2)\n        self.click_formula_bar()\n        self.human_key_press(key_codes.NUMBER_1)\n        self.human_key_press(key_codes.NUMBER_2)\n        self.human_key_press(key_codes.NUMBER_3)\n\n        # * He waits for more than 25 seconds, and notes that the recalc ends.\n        sleep(25)\n        self.wait_for_spinner_to_stop(timeout_seconds=20)\n\n        # * The formula bar still contains \"123\"\n        self.assert_formula_bar_contains(\"123\")\n"
  },
  {
    "path": "dirigible/fts/tests/test_2690_FocusShouldStartInGrid.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom functionaltest import FunctionalTest\nimport key_codes\n\n\nclass test_2690_FocusShouldStartInGrid(FunctionalTest):\n\n    def test_focus(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * The focus is initially in A1\n        # (not in text editor where it is annoying since clicking on\n        # grid then causes unwanted recalcs. Especially annoying after\n        # loading a large sheet)\n\n        # Hence, Harold starts typing...\n        self.human_key_press(key_codes.NUMBER_1)\n        self.human_key_press(key_codes.NUMBER_2)\n        self.human_key_press(key_codes.NUMBER_3)\n\n        # * The formula he typed appears in cell A1's edit box\n        self.wait_for_cell_to_enter_edit_mode(1, 1)\n        self.wait_for_cell_editor_content('123')\n\n"
  },
  {
    "path": "dirigible/fts/tests/test_2691_AllowSettingValuesFromUsercodeBeforeLoadConstants.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom textwrap import dedent\n\ntry:\n    import unittest2 as unittest\nexcept ImportError:\n    import unittest\n\nfrom functionaltest import FunctionalTest\n\n\nclass Test_2691_AllowSettingValuesFromUsercodeBeforeLoadConstants(FunctionalTest):\n\n    def test_load_constants_doesnt_trash_cells(self):\n        # * Harold logs in and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He adds stuff to the start of the usercode to set a value on a cell, before load_constants\n        #   (which previously erroneously cleared cells with no set formula)\n        self.prepend_usercode(dedent(\"\"\"\n            worksheet[1, 1].value = 'wibble'\n        \"\"\"))\n\n        # * When the recalc is completed, the cell shows the value\n        self.wait_for_cell_value(1, 1, 'wibble')"
  },
  {
    "path": "dirigible/fts/tests/test_2701_NameResolutionWorks.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom textwrap import dedent\n\nfrom functionaltest import FunctionalTest\n\n\nclass Test_2701_NameResolutionWorks(FunctionalTest):\n\n    def test_name_resolution(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He writes some usercode that does something to test\n        #   name resolution.\n        self.enter_usercode(dedent(\"\"\"\n            from urllib2 import urlopen, URLError\n            try:\n                connection = urlopen(\"http://www.google.com/\")\n                worksheet[1, 1].value = len(connection.read())\n                connection.close()\n            except URLError, e:\n                worksheet[1, 1].value = e.reason[1]\n            except Exception, e:\n                worksheet[1, 1].value = str(e)\n        \"\"\"))\n\n        # * It runs OK.\n        self.wait_for_spinner_to_stop()\n        size = int(self.get_cell_text(1, 1))\n        self.assertTrue(size > 0)\n"
  },
  {
    "path": "dirigible/fts/tests/test_2702_HttpsInChrootJail.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom textwrap import dedent\n\nfrom functionaltest import FunctionalTest\n\n\nclass Test_2702_HttpsInChrootJail(FunctionalTest):\n\n    def test_numpy_tutorial_create_arrays(self):\n        # * Harold wants to access https URLs from his Dirigible sheets\n        # * He logs in and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * .. and adds a get for a https URL in an S3 bucket\n        self.prepend_usercode(dedent('''\n            from urllib2 import urlopen\n            connection = urlopen('https://s3.amazonaws.com/oecd-data/oecd-data.csv')\n            content = connection.read()\n            worksheet.A1.value = 'Canada' in content\n        '''))\n\n        # * and notes that he gets the expected content\n        self.wait_for_cell_value(1, 1, 'True')\n\n\n"
  },
  {
    "path": "dirigible/fts/tests/test_2704_OldStyleClassesInTheGrid.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nimport re\nfrom textwrap import dedent\n\nfrom functionaltest import FunctionalTest\n\n\nclass Test_2704_OldStyleClassesInTheGrid(FunctionalTest):\n\n    def test_put_old_style_classes_in_grid(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He defines an old-style class in the usercode\n        self.enter_usercode(dedent('''\n            class Basilisk():\n                pass\n            load_constants(worksheet)\n            evaluate_formulae(worksheet)\n        '''))\n\n        # * He puts an instance of it in the grid\n        self.enter_cell_text(1, 1, \"=Basilisk()\")\n\n        # * The formatted value is something sane.\n        self.wait_for_cell_value(1, 1, re.compile(r'<__builtin__.Basilisk instance at 0x.*>'))\n\n        # * He references the class itself from another cell, just for kicks.\n        self.enter_cell_text(1, 2, \"=Basilisk\")\n\n        # * The formatted value is equally sane.\n        self.wait_for_cell_value(1, 2, '__builtin__.Basilisk')\n\n        # * He refreshes the page.\n        self.selenium.refresh()\n        self.wait_for_grid_to_appear()\n\n        # * Everything still looks OK.\n        self.wait_for_cell_value(1, 1, re.compile(r'<__builtin__.Basilisk instance at 0x.*>'))\n        self.wait_for_cell_value(1, 2, '__builtin__.Basilisk')\n\n        # * Disappointed at not being about to break Dirigible, he goes out to take\n        #   potshots at the Goodyear blimp with a shotgun.\n"
  },
  {
    "path": "dirigible/fts/tests/test_2711_ImportExcel.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom functionaltest import FunctionalTest, PAGE_LOAD_TIMEOUT, Url\n\nimport os\n\nSHEET1 = 'first sheet'\nSHEET2 = '2nd'\nEMPTY_SHEET = 'empty sheet'\n\n\nclass Test_2711_Import_Excel(FunctionalTest):\n\n    def test_can_import_excel_values_only_from_sheet_page(self):\n        # Harold has an excel file he wants to import into a cloud-based\n        # python-infused spreadsheet\n\n        # * Harold logs in to Dirigible\n        self.login_and_create_new_sheet()\n\n        # * He spots the 'import' button\n        self.wait_for_element_visibility('id=id_import_button', True)\n        self.assertEquals(\n                self.selenium.get_attribute('id=id_import_button@alt'),\n                \"Import a file\"\n        )\n        self.assertEquals(\n                self.selenium.get_attribute('id=id_import_button@title'),\n                \"Import a file\"\n        )\n\n        # * He clicks the import button\n        self.selenium.click('id=id_import_button')\n\n        # * He is presented with a jquery dialog that contains a file input element\n        self.wait_for_element_visibility('id=id_import_form', True)\n        self.wait_for_element_visibility('id=id_import_form_file', True)\n        self.wait_for_element_visibility('id=id_import_form_upload_csv_button', False)\n        self.wait_for_element_visibility('id=id_import_form_upload_xls_values_button', False)\n        self.wait_for_element_visibility('id=id_import_form_cancel_button', True)\n        \n        # * Harold panics at seeing something new, and clicks the cancel button\n        self.selenium.click('id=id_import_form_cancel_button')\n\n        # the dialog disappears\n        self.wait_for_element_visibility('id=id_import_form', False)\n\n        # Harold, keen to get data imported, summons up the strength to try again\n        self.selenium.click('id=id_import_button')\n\n        # the dialog reappears.\n        self.wait_for_element_visibility('id=id_import_form', True)\n\n        # * He clicks on the file browse button. He is presented with a\n        #   file-open dialog, and chooses a suitable excel file\n        file_name = os.path.join(\n                os.path.dirname(__file__),\n                'test_data', 'T2711-import-excel.xls'\n        )\n        self.set_filename_for_upload(file_name, 'id=id_import_form_file')\n\n        # the upload excel buttons appear\n        self.wait_for_element_visibility(\n                'id=id_import_form_upload_xls_values_button', True)\n\n        # so he clicks one\n        self.selenium.click('id=id_import_form_upload_xls_values_button')\n\n        # * ...and gets redirected to his dashboard\n        self.selenium.wait_for_page_to_load(10000)\n        self.selenium.open(Url.ROOT)\n        self.selenium.wait_for_page_to_load(10000)\n\n        # He notices 3 new sheets added to his dashboard\n        page_text = self.selenium.get_html_source()\n        self.assertTrue('T2711-import-excel - first sheet' in page_text)\n        self.assertTrue('T2711-import-excel - 2nd' in page_text)\n        self.assertTrue('T2711-import-excel - error sheet' in page_text)\n        self.assertTrue('T2711-import-excel - empty sheet' not in page_text)\n\n        # He visits the first one to check that the values there are\n        # the same as the ones in the Excel file\n        self.selenium.click('link=T2711-import-excel - first sheet')\n        self.selenium.wait_for_page_to_load(10000)\n        self.wait_for_grid_to_appear()\n\n        self.wait_for_cell_value(1, 1, \"Spell\")\n        self.wait_for_cell_value(2, 1, \"Arc\")\n        self.wait_for_cell_value(3, 1, \"Bar\")\n        self.wait_for_cell_value(1, 2, \"attack\")\n        self.wait_for_cell_value(14, 8, \"E\")\n\n        # he goes back to the dashboard\n        self.selenium.open(Url.ROOT)\n        self.selenium.wait_for_page_to_load(10000)\n\n        # He visits the second sheet to check that the values there are\n        # the same as the ones in the Excel file\n        self.selenium.click('link=T2711-import-excel - 2nd')\n        self.selenium.wait_for_page_to_load(10000)\n        self.wait_for_grid_to_appear()\n\n        self.wait_for_cell_value(1, 1, \"sauce\")\n        self.wait_for_cell_value(3, 4, \"27.5\")\n\n        # he goes back to the dashboard\n        self.selenium.open(Url.ROOT)\n        self.selenium.wait_for_page_to_load(10000)\n\n        # He visits the third sheet to check that the values there are\n        # the same as the ones in the Excel file\n        self.selenium.click('link=T2711-import-excel - error sheet')\n        self.selenium.wait_for_page_to_load(10000)\n        self.wait_for_grid_to_appear()\n\n        self.wait_for_cell_value(1, 1, \"1979-10-08 00:00:00\")\n        self.click_on_cell(2, 1)\n        self.wait_for_formula_bar_contents(\"=#NUM!\")\n        self.click_on_cell(3, 1)\n        self.wait_for_formula_bar_contents(\"=#DIV/0!\")\n\n\n    def test_bad_files_are_gracefully_handled(self):\n        # Harold thinks that if he imports an image file,\n        # it will appear in his spreadsheet\n\n        # * He logs in to Dirigible and creates a nice shiny new sheet\n        self.login_and_create_new_sheet()\n        sheet_url = self.browser.current_url\n\n        # * He clicks the import button\n        self.selenium.click('id=id_import_button')\n\n        # * He is presented with a jquery dialog that contains a file input\n        # element\n        self.wait_for_element_visibility('id=id_import_form', True)\n\n        # * He clicks on the browse button\n        # * He is presented with a file-open dialog, and chooses his image file\n        file_name = os.path.join(\n                os.path.dirname(__file__),\n                'test_data', 'T2711-badly-named-png.xls'\n        )\n        self.set_filename_for_upload(file_name, 'id=id_import_form_file')\n\n        # He clicks the upload excel values only button\n        self.selenium.click('id=id_import_form_upload_xls_values_button')\n\n        # * ...and waits for the page to refresh\n        self.selenium.wait_for_page_to_load(PAGE_LOAD_TIMEOUT)\n\n        # * He is presented with an appropriate error page\n        self.assertEquals(\n                self.browser.title, \"Excel Import Error: Dirigible\")\n        self.assertEquals(\n                self.get_text(\"id=id_server_error_title\"),\n                \"Could not import Excel file\"\n        )\n        error_text = self.get_text(\"id=id_server_error_text\")\n        msg = \"Sorry, the file you uploaded was not imported\"\n        self.assertTrue(msg in error_text)\n\n        # * There is a link back to his account page, which he follows\n        self.click_link('id_account_link')\n\n        # And finds himself back on his sheet page.\n        self.assertEquals(self.browser.current_url, Url.ROOT)\n\n"
  },
  {
    "path": "dirigible/fts/tests/test_2712_ImportCSV.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom functionaltest import FunctionalTest, PAGE_LOAD_TIMEOUT\n\nimport os\n\n\nclass Test_2712_ImportCSV(FunctionalTest):\n\n    def test_can_import_excel_generated_csv_to_cursor_position(self):\n        file_name = 'excel_generated_csv.csv'\n        # Harold has a csv file he wants to import into a cloud-based\n        # python-infused spreadsheet\n\n        # * Harold logs in to Dirigible and creates a nice shiny new sheet\n        self.login_and_create_new_sheet()\n\n        # * After weeks of frustration at being unable to get any data into the app,\n        #  it's with great joy that he spots a new button called 'import'\n        self.wait_for_element_visibility('id=id_import_button', True)\n        self.assertEquals(\n            self.selenium.get_attribute('id=id_import_button@alt'),\n            \"Import a file\"\n        )\n        self.assertEquals(\n            self.selenium.get_attribute('id=id_import_button@title'),\n            \"Import a file\"\n        )\n\n        #  With preternatural insightfulness, he guesses the button will import\n        #  to the current cursor position. Accordingly, he gives himself a\n        #  little wriggle room\n\n        self.click_on_cell(2, 2)\n\n        # * He clicks the import CSV button\n        self.selenium.click('id=id_import_button')\n\n        # * He is presented with a jquery dialog that contains a file input element\n        self.wait_for_element_visibility('id=id_import_form', True)\n        self.wait_for_element_visibility('id=id_import_form_file', True)\n        self.wait_for_element_visibility('id=id_import_form_upload_csv_button', False)\n        self.wait_for_element_visibility('id=id_import_form_upload_xls_values_button', False)\n        self.wait_for_element_visibility('id=id_import_form_cancel_button', True)\n\n        #Harold chooses a file, but changes his mind and clicks cancel\n        file_name = os.path.join(\n            os.path.dirname(__file__), 'test_data', file_name\n        )\n        self.set_filename_for_upload(file_name, 'id=id_import_form_file')\n        self.selenium.click('id=id_import_form_cancel_button')\n\n        # the dialog disappears\n        self.wait_for_element_visibility('id=id_import_form', False)\n\n        # harold, keen to get data imported, summons up the strength to try again\n        self.selenium.click('id=id_import_button')\n\n        # the dialog reappears\n        self.wait_for_element_visibility('id=id_import_form', True)\n\n        # his previous file choice doesn't\n        self.assertEquals(\n                self.selenium.get_value('id=id_import_form_file'),\n                ''\n        )\n\n        # * He clicks on the browse button * He is presented with a file-open\n        # dialog, and chooses a suitable csv file\n        self.set_filename_for_upload(file_name, 'id=id_import_form_file')\n\n        # He spots a radio button, which is defaulted to the 'excel' option\n        self.wait_for_element_visibility(\n                'css=input[type=\"radio\"][name=\"csv_encoding\"][value=\"excel\"]', True\n        )\n        self.wait_for_element_visibility(\n                'css=input[type=\"radio\"][name=\"csv_encoding\"][value=\"other\"]', True\n        )\n        self.assertEquals(\n            self.selenium.get_value(\n                'css=input[type=\"radio\"][name=\"csv_encoding\"][value=\"excel\"]'\n            ),\n            'on'\n        )\n\n        # so he clicks the upload button\n        self.wait_for_element_visibility('id=id_import_form_upload_csv_button', True)\n        self.selenium.click('id=id_import_form_upload_csv_button')\n\n        # * ...and waits for the page to refresh\n        self.selenium.wait_for_page_to_load(PAGE_LOAD_TIMEOUT)\n        self.wait_for_grid_to_appear()\n\n        # * and is mightily pleased when his data appears at the cursor position.\n        self.wait_for_cell_value(2, 2, 'some text')\n        self.wait_for_cell_value(3, 2, 'text with quotes \"\\'\"\"\\'\"')\n\n        # * In order to check that the cell with a carraige return is imported\n        #   ok, he has to check its value via the console, since the \\n is\n        #   converted to something else for display in the cell\n        #self.wait_for_cell_value(4, 2, 'text with a \\ncarriage return')\n        self.append_usercode('print worksheet.D2.value == \"text with a \\\\ncarriage return\"')\n        self.wait_for_console_content('True')\n\n        self.wait_for_cell_value(2, 3, 'some european characters:')\n        self.wait_for_cell_value(3, 3, u'Herg\\xe9')\n\n        self.wait_for_cell_value(2, 4, 'some european money:')\n        self.wait_for_cell_value(3, 4, u'pounds: \\xa3')\n        self.wait_for_cell_value(4, 4, u'euros : \\u20ac')\n        self.wait_for_cell_value(2, 5, 'numbers')\n        self.wait_for_cell_value(2, 6, '1')\n        self.wait_for_cell_value(3, 6, '2')\n        self.wait_for_cell_value(4, 6, '3000000000')\n\n\n    def test_can_import_utf8_csv(self):\n        # Harold has a kawaii csv file he wants to import into a cloud-based\n        # python-infused spreadsheet\n        file_name = 'japanese.csv'\n\n        # * He creates a new sheet and clicks the import CSV button\n        self.login_and_create_new_sheet()\n        self.selenium.click('id=id_import_button')\n\n        # the dialog reappears\n        self.wait_for_element_visibility('id=id_import_form', True)\n\n        # * He clicks on the browse button * He is presented with a file-open\n        # dialog, and chooses a suitable csv file\n        file_name = os.path.join(\n            os.path.dirname(__file__), 'test_data', file_name\n        )\n        self.set_filename_for_upload(file_name, 'id=id_import_form_file')\n        # He spots a radio button, which is defaulted to the 'excel' option\n        self.wait_for_element_visibility(\n                'css=input[type=\"radio\"][name=\"csv_encoding\"][value=\"excel\"]', True\n        )\n        self.wait_for_element_visibility(\n                'css=input[type=\"radio\"][name=\"csv_encoding\"][value=\"other\"]', True\n        )\n        self.assertEquals(\n            self.selenium.get_value(\n                'css=input[type=\"radio\"][name=\"csv_encoding\"][value=\"excel\"]'\n            ),\n            'on'\n        )\n\n        # so he changes the radio button option to 'other'\n        self.selenium.check(\n                'css=input[type=\"radio\"][name=\"csv_encoding\"][value=\"other\"]'\n        )\n\n        # and clicks the upload button\n        self.wait_for_element_visibility('id=id_import_form_upload_csv_button', True)\n        self.selenium.click('id=id_import_form_upload_csv_button')\n\n        # * ...and waits for the page to refresh\n        self.selenium.wait_for_page_to_load(PAGE_LOAD_TIMEOUT)\n        self.wait_for_grid_to_appear()\n\n        # * and is mightily pleased when his kanji appear\n        self.wait_for_cell_value(1, 1, u'\\u65b0\\u4e16\\u7d00\\u30a8\\u30f4\\u30a1\\u30f3\\u30b2\\u30ea\\u30aa\\u30f3')\n\n\n\n    def test_bad_files_are_gracefully_handled(self):\n        # Harold thinks that if he imports an image file,\n        # it will appear in his spreadsheet\n\n        file_name = os.path.join(\n            os.path.dirname(__file__), 'test_data', 'import_csv_button.png')\n\n        # * He logs in to Dirigible and creates a nice shiny new sheet\n        self.login_and_create_new_sheet()\n        sheet_url = self.browser.current_url\n\n        # * He clicks the import toolbar button\n        self.selenium.click('id=id_import_button')\n\n        # * He is presented with a jquery dialog that contains a file input element\n        self.wait_for_element_visibility('id=id_import_form', True)\n        self.wait_for_element_visibility('id=id_import_form_file', True)\n        self.wait_for_element_visibility('id=id_import_form_cancel_button', True)\n\n        # * He clicks on the browse button\n        # * He is presented with a file-open dialog, and chooses his image file\n        self.set_filename_for_upload(file_name, 'id=id_import_form_file')\n\n        # He clicks the upload button\n        self.wait_for_element_visibility('id=id_import_form_upload_csv_button', True)\n        self.selenium.click('id=id_import_form_upload_csv_button')\n\n        # * ...and waits for the page to refresh\n        self.selenium.wait_for_page_to_load(PAGE_LOAD_TIMEOUT)\n\n        # * He is presented with an appropriate error page\n        self.assertEquals(self.browser.title, \"CSV Import Error: Dirigible\")\n        self.assertEquals(\n            self.get_text(\"id=id_server_error_title\"),\n            \"Could not import CSV file\"\n        )\n        error_text = self.get_text(\"id=id_server_error_text\")\n        msg = \"Sorry, the file you uploaded was not in a recognised CSV format\"\n        self.assertTrue(msg  in error_text)\n\n        # * There is a link back to the sheet page, which he follows\n        self.click_link('id_sheet_link')\n\n        # And finds himself back on his sheet page.\n        self.wait_for_grid_to_appear()\n        self.assertEquals(self.browser.current_url, sheet_url)\n\n"
  },
  {
    "path": "dirigible/fts/tests/test_2726_FormulaBarTextSelection.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom functionaltest import FunctionalTest\n\nfrom key_codes import LEFT, LETTER_S\n\n\nclass Test_2726_FormulaBarSelectionDefect(FunctionalTest):\n\n    def test_can_select_text_in_formula_bar(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        long_string = '1234567890' * 10\n        self.enter_cell_text(1, 1, long_string)\n        self.wait_for_cell_value(1, 1, long_string)\n        self.click_on_cell(1, 1)\n\n        self.selenium.click('id=id_formula_bar')\n        self.human_key_press(LEFT)\n        self.human_key_press(LEFT)\n        self.human_key_press(LEFT)\n        self.human_key_press(LEFT)\n        self.human_key_press(LEFT)\n        self.human_key_press(LEFT)\n\n        self.selenium.click('id=id_formula_bar')\n\n        self.human_key_press(LETTER_S)\n        self.human_key_press(LETTER_S)\n        self.assertTrue(\n            self.selenium.get_value(\n                self.get_formula_bar_locator()).endswith(\n                '1234ss567890'\n            )\n        )\n"
  },
  {
    "path": "dirigible/fts/tests/test_2734_ClearCells.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\ntry:\n    import unittest2 as unittest\nexcept ImportError:\n    import unittest\n\nfrom functionaltest import FunctionalTest\nimport key_codes\nfrom textwrap import dedent\n\n\nclass Test_2734_ClearCells(FunctionalTest):\n\n    def test_delete_key_clears_selected_cells(self):\n        self.assert_key_deletes_cells(key_codes.DELETE)\n\n\n    def test_backspace_key_clears_selected_cells(self):\n        self.assert_key_deletes_cells(key_codes.BACKSPACE)\n\n\n    def assert_key_deletes_cells(self, key_code):\n        # * Harold logs in and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He enters some data in A1:A3\n        self.enter_cell_text(1, 1, 'a1')\n        self.enter_cell_text(1, 2, 'a2')\n        self.enter_cell_text(1, 3, 'a3')\n        self.wait_for_cell_value(1, 3, 'a3')\n\n        # * He clicks on A1 and hits delete\n        self.click_on_cell(1, 1)\n        self.human_key_press(key_code)\n\n        # * He sees the value in A1 disappear while the others remain\n        self.wait_for_cell_value(1, 1, '')\n        self.wait_for_cell_value(1, 2, 'a2')\n        self.wait_for_cell_value(1, 3, 'a3')\n\n        # * He selects the range a2:a3\n        self.select_range_with_shift_click((1, 2), (1, 3))\n\n        # He hits delete\n        self.human_key_press(key_code)\n\n        # * He sees that all the cells are now cleared\n        self.wait_for_cell_value(1, 1, '')\n        self.wait_for_cell_value(1, 2, '')\n        self.wait_for_cell_value(1, 3, '')\n\n\n    def test_delete_key_while_editing_still_does_what_it_should(self):\n        # * Harold logs in and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He enters three characters in A1\n        self.open_cell_for_editing(1, 1)\n        self.human_key_press(key_codes.NUMBER_1)\n        self.human_key_press(key_codes.NUMBER_2)\n        self.human_key_press(key_codes.NUMBER_3)\n\n        # * He moves left twice\n        self.human_key_press(key_codes.LEFT)\n        self.human_key_press(key_codes.LEFT)\n\n        # He hits delete\n        self.human_key_press(key_codes.DELETE)\n\n        # the middle character is now missing\n        self.wait_for_cell_editor_content('13')\n\n\n    def test_backspace_key_while_editing_still_does_what_it_should(self):\n        # * Harold logs in and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He enters three characters in A1\n        self.open_cell_for_editing(1, 1)\n        self.human_key_press(key_codes.NUMBER_1)\n        self.human_key_press(key_codes.NUMBER_2)\n        self.human_key_press(key_codes.NUMBER_3)\n\n        # * He moves left once\n        self.human_key_press(key_codes.LEFT)\n\n        # He hits backspace\n        self.human_key_press(key_codes.BACKSPACE)\n\n        # the middle character is now missing\n        self.wait_for_cell_editor_content('13')\n\n\n    def test_can_clear_cell_from_usercode(self):\n        # * Harold logs in and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He enters some data in A1:A3\n        self.enter_cell_text(1, 1, 'a1')\n        self.enter_cell_text(1, 2, 'a2')\n        self.enter_cell_text(1, 3, 'a3')\n        self.wait_for_cell_value(1, 3, 'a3')\n\n        # * He tries to use the clear() function from usercode on a cell\n        # and then tries to access some of the supposedly cleared attributes of the cell\n        self.prepend_usercode(dedent('''\n            worksheet.a1.error = 'harold puts a deliberate pointless error in'\n\n            worksheet.a1.clear()\n\n            worksheet.b1.formula = str(worksheet.a1.value)\n            worksheet.b2.formula = str(worksheet.a1.formula)\n            worksheet.b3.formula = str(worksheet.a1.formatted_value)\n            worksheet.b4.formula = str(worksheet.a1.error)\n        '''))\n\n        # * He sees the value in a1 disappear\n        self.wait_for_cell_value(1, 1, '')\n        self.wait_for_cell_value(1, 2, 'a2')\n        self.wait_for_cell_value(1, 3, 'a3')\n\n        # * He sees his little investigations also produce the expected results\n        self.wait_for_cell_value(2, 1, '<undefined>')\n        self.wait_for_cell_value(2, 2, 'None')\n        self.wait_for_cell_value(2, 3, '')\n        self.wait_for_cell_value(2, 4, 'None')\n\n\n    def test_can_clear_cell_range_from_usercode(self):\n        # * Harold logs in and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He enters some data in A1:A3\n        self.enter_cell_text(1, 1, 'a1')\n        self.enter_cell_text(1, 2, 'a2')\n        self.enter_cell_text(1, 3, 'a3')\n        self.wait_for_cell_value(1, 3, 'a3')\n\n        # * He tries to use the clear() function from usercode on a cell range\n        self.prepend_usercode(dedent('''\n            worksheet.a1.error = 'harold puts a deliberate pointless error in'\n            worksheet.a2.error = 'harold puts another deliberate pointless error in'\n\n            worksheet.cell_range(\"a1:a2\").clear()\n\n            worksheet.b1.formula = str(worksheet.a1.value)\n            worksheet.b2.formula = str(worksheet.a1.formula)\n            worksheet.b3.formula = str(worksheet.a1.formatted_value)\n            worksheet.b4.formula = str(worksheet.a1.error)\n            worksheet.c1.formula = str(worksheet.a2.value)\n            worksheet.c2.formula = str(worksheet.a2.formula)\n            worksheet.c3.formula = str(worksheet.a2.formatted_value)\n            worksheet.c4.formula = str(worksheet.a2.error)\n        '''))\n        # * He sees the value in a1 and a2 disappear\n        self.wait_for_cell_value(1, 1, '')\n        self.wait_for_cell_value(1, 2, '')\n        self.wait_for_cell_value(1, 3, 'a3')\n\n        # * He sees his little investigations also produce the expected results\n        self.wait_for_cell_value(2, 1, '<undefined>')\n        self.wait_for_cell_value(2, 2, 'None')\n        self.wait_for_cell_value(2, 3, '')\n        self.wait_for_cell_value(2, 4, 'None')\n\n        self.wait_for_cell_value(3, 1, '<undefined>')\n        self.wait_for_cell_value(3, 2, 'None')\n        self.wait_for_cell_value(3, 3, '')\n        self.wait_for_cell_value(3, 4, 'None')\n"
  },
  {
    "path": "dirigible/fts/tests/test_2735_CtrlKeysArePassedOnToBrowser.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom __future__ import with_statement\n\nfrom functionaltest import FunctionalTest\nimport key_codes\n\n\nclass Test_2735_CtrlKeysArePassedToBrowser(FunctionalTest):\n\n    def test_ctrl_t(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He clicks the grid then types ctrl-t\n        self.click_on_cell(1, 1)\n        with self.key_down(key_codes.CTRL):\n            self.human_key_press(key_codes.LETTER_T)\n\n        # * A new tab has been opened.  NB no way to test this\n        #   was found after quite a while investigating.\n        # * Harold switches back\n        self.selenium.select_window('null')\n\n        # * The current cell is not being edited.\n        self.assert_cell_is_current_but_not_editing(1, 1)\n        # * The current cell does not contain a 't'\n        self.assertEquals('', self.get_cell_text(1, 1))\n\n"
  },
  {
    "path": "dirigible/fts/tests/test_2741_Xlrd.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom functionaltest import FunctionalTest\n\n\nclass Test_2741_Xlrd(FunctionalTest):\n\n    def test_can_use_xlrd(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # He imports xlrd\n        self.prepend_usercode(\"import xlrd\")\n        self.append_usercode(\"worksheet.A1.value = 4\")\n\n        # * and it works\n        self.wait_for_cell_value(1, 1, '4')\n\n"
  },
  {
    "path": "dirigible/fts/tests/test_2749_DisallowArbitraryKeysForWorksheet.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom functionaltest import FunctionalTest\n\n\nclass Test_2749_DisallowArbitraryKeysForWorksheet(FunctionalTest):\n\n    def test_cant_use_silly_worksheet_keys(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He writes usercode to use a non-cell-reference key with the worksheet\n        self.append_usercode(\"worksheet['fred'].value = 23\")\n\n        # * He gets an error\n        self.wait_for_console_content(\"KeyError: 'fred' is not a valid cell location\")\n"
  },
  {
    "path": "dirigible/fts/tests/test_2751_UsefulModules.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom textwrap import dedent\n\nfrom functionaltest import FunctionalTest\n\n\nclass test_2751_UsefulModules(FunctionalTest):\n\n    def test_can_import_useful_modules(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # He writes some usercode that imports a bunch of modules\n        # and then sets a cell to a value to prove that it worked.\n        self.prepend_usercode(dedent(\"\"\"\n            ## pycrypto\n            from Crypto.Hash import MD5\n            ## sqlalchemy\n            from sqlalchemy import *\n            ## lxml\n            from lxml import *\n            ## rdflib\n            from rdflib.graph import Graph\n            ## geopy\n            from geopy import *\n            ## BeautifulSoup\n            from BeautifulSoup import *\n            ## mechanize\n            from mechanize import *\n\n            worksheet.A1.value = 21\n        \"\"\"))\n\n        # The cell has the right value\n        self.wait_for_cell_value(1, 1, \"21\")"
  },
  {
    "path": "dirigible/fts/tests/test_2758_LoadGridDataOnDemand.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom __future__ import with_statement\n\nfrom functionaltest import FunctionalTest\nimport key_codes\n\nfrom textwrap import dedent\n\n\nclass Test_2758_LoadGridDataOnDemand(FunctionalTest):\n\n    def test_grid_data_loaded_on_demand_for_rows(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * Using usercode, he puts a blob of data far down and to\n        #   the right in the sheet.\n        self.append_usercode(dedent(\"\"\"\n            for col in range(1, 53):\n                for row in range(600, 1001):\n                    worksheet[col, row].value = \"(%s, %s)\" % (col, row)\n        \"\"\"))\n\n        # * He waits for the recalc to complete.\n        self.wait_for_spinner_to_stop()\n        # He makes sure the grid has focus for the subsequent keypress\n        self.click_on_cell(1, 1)\n\n        # * then types Ctrl-End\n        with self.key_down(key_codes.CTRL):\n            self.human_key_press(key_codes.END)\n\n        # * The grid scrolls down and to the right, but the cells are initially\n        #   empty\n        self.wait_for_cell_to_be_visible(52, 1000)\n        self.wait_for_cell_value(52, 1000, '')\n\n        # * A \"Buffering\" window appears, relatively briefly\n        self.wait_for_element_visibility(\"id=id_buffering_message\", True)\n\n        # * When the \"buffering\" window disappears, the data is there.\n        self.wait_for_element_visibility(\"id=id_buffering_message\", False)\n        self.wait_for_cell_value(52, 1000, '(52, 1000)')\n\n"
  },
  {
    "path": "dirigible/fts/tests/test_2762_PythonConversion.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom functionaltest import FunctionalTest\n\n\nclass Test_2762_PythonConversion(FunctionalTest):\n\n    def test_formulas_work_properly(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # the following formula was causing errors\n        self.enter_cell_text(1, 1, '10')\n        self.enter_cell_text(1, 2, '=[x * A1 for x in range(5)]')\n        self.wait_for_cell_value(1, 2, '[0, 10, 20, 30, 40]')\n\n        # as was this one\n        self.enter_cell_text(2, 1, '10')\n        self.enter_cell_text(2, 2, '={1 -> B1}[1]')\n        self.wait_for_cell_value(2, 2, '10')\n\n        # and this\n        self.enter_cell_text(3, 1, '10')\n        self.enter_cell_text(3, 2, '=(lambda x -> C1 * x)(2)')\n        self.wait_for_cell_value(3, 2, '20')\n\n"
  },
  {
    "path": "dirigible/fts/tests/test_2770_ClearDependentCellErrors.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom functionaltest import FunctionalTest\n\n\nclass Test_2770_ClearDependentCellErrors(FunctionalTest):\n\n    def test_dependent_cell_errors_are_cleared(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He enters \"=1/0\" into A1\n        self.enter_cell_text(1, 1, '=1/0')\n        self.wait_for_spinner_to_stop()\n        # * He enters \"=A1 + 1\" into A2\n        self.enter_cell_text(1, 2, '=A1 + 1')\n        self.wait_for_spinner_to_stop()\n        # * He enters \"=A2 + 1\" into A3\n        self.enter_cell_text(1, 3, '=A2 + 1')\n        self.wait_for_spinner_to_stop()\n\n        # * He confirms that both A1 and A2 have errors\n        self.assert_cell_has_error(1, 1, 'ZeroDivisionError: division by zero')\n        self.assert_cell_has_error(\n            1, 2,\n            \"TypeError: unsupported operand type(s) for +: 'Undefined' and 'int'\"\n        )\n        self.assert_cell_has_error(\n            1, 3,\n            \"TypeError: unsupported operand type(s) for +: 'Undefined' and 'int'\"\n        )\n\n        # * He changes A1 to \"=1\"\n        self.enter_cell_text(1, 1, '=1')\n\n        # * A1's error will clear.\n        self.wait_for_cell_value(1, 1, '1')\n        # * A2's should too\n        self.wait_for_cell_value(1, 2, '2')\n        # * and A3's should too\n        self.wait_for_cell_value(1, 3, '3')\n\n"
  },
  {
    "path": "dirigible/fts/tests/test_2774_ExportCSV.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom __future__ import with_statement\n\nfrom os import path\nimport urllib2\nfrom urlparse import urljoin\n\nfrom functionaltest import FunctionalTest, PAGE_LOAD_TIMEOUT\n\n\nclass Test_2774_ExportCSV(FunctionalTest):\n\n    def test_can_export_csv(self):\n        # * Harold logs in to Dirigible and creates a nice shiny new sheet\n        sheet_id = self.login_and_create_new_sheet()\n\n        # * He puts some data including formulae and usercode-calculated stuff into a spreadsheet.\n        #   Some of the data has commas and the like in it, and there is a blank column to the\n        #   left of the data and a blank row above it.  There are also non-string cell contents.\n        self.enter_cell_text(2, 2,\n                \"Data at A2, from a constant\")\n        self.enter_cell_text(2, 3,\n                \"Data at A3, with a \\\\n that might need escaping\")\n        self.enter_cell_text(2, 4,\n                \"Data at A4, with 'single quotes'\")\n        self.enter_cell_text(2, 5,\n                'Data at A5, with \"double quotes\"')\n        self.enter_cell_text(2, 6,\n                u'Data at A6, with some unicode: Sacr\\xe9 bleu! The \\xa3 is expensive, compared to the \\u20ac!')\n        self.enter_cell_text(3, 2, \"=2+2\")\n        self.append_usercode(\"worksheet.E4.value = 'hellooooo there!'\")\n        self.append_usercode(\n                \"worksheet.E5.value = ['list item 1', 'list item 2', 3]\")\n        self.append_usercode(\n                \"worksheet.E6.value = {'oats': 'a cereal which in England is fed to horses, but in Scotland forms the sustenance of the nation'}\")\n        self.append_usercode(\n                \"worksheet.E7.value = lambda x : 2 * x\")\n\n        self.wait_for_spinner_to_stop()\n\n        # * He sees a button that talks about exporting CSVs\n        self.wait_for_element_visibility('id=id_export_button', True)\n        self.assertEquals(\n                self.selenium.get_attribute('id=id_export_button@alt'),\n                \"Download as CSV file\"\n        )\n        self.assertEquals(\n                self.selenium.get_attribute('id=id_export_button@title'),\n                \"Download as CSV file\"\n        )\n        # * He clicks on it, and sees a popup dialog with two links and a close button\n        self.selenium.click('id=id_export_button')\n        self.wait_for_element_visibility('id=id_export_dialog', True)\n        self.wait_for_element_visibility('id=id_export_csv_excel_version', True)\n        self.wait_for_element_visibility('id=id_export_csv_unicode_version', True)\n        self.wait_for_element_visibility('id=id_export_dialog_close_button', True)\n        self.assertEquals(self.selenium.get_value('id=id_export_dialog_close_button'), \"Close\")\n\n        # Were he to click on the former, his browser would do whatever it normally\n        # does when a user starts a download. This is too hard to test with\n        # Selenium.  Lets go shopping^W^W use urllib2\n        download_url = self.selenium.get_attribute('id=id_export_csv_excel_version@href')\n        download_url = urljoin(self.browser.current_url, download_url)\n\n        stream = self.get_url_with_session_cookie(download_url)\n        self.assertEquals(stream.info().gettype(), \"text/csv\")\n        sheet_name = 'Sheet %s' % (sheet_id,)\n        self.assertEquals(\n                stream.info()['Content-Disposition'],\n                'attachment; filename=%s.csv' % (sheet_name,)\n        )\n\n        expected_file_name = path.join(\n                path.dirname(__file__),\n                \"test_data\", \"expected_csv_file.csv\"\n        )\n        with open(expected_file_name) as expected_file:\n            self.assertEquals(\n                stream.read().replace(\"\\r\\n\", \"\\n\"),\n                expected_file.read().replace(\"\\r\\n\", \"\\n\")\n            )\n\n        # The file downloaded, he closes the dialog.\n        self.selenium.click('id=id_export_dialog_close_button')\n        self.wait_for_element_visibility('id=id_export_dialog', False)\n\n\n\n    def test_can_export_unicode(self):\n        #Harold-san has a sheet-u-des which has some zugoi kanji in:\n        sheet_id = self.login_and_create_new_sheet()\n        some_kanji = u'\\u30bc\\u30ed\\u30a6\\u30a3\\u30f3\\u30b0'\n        self.enter_cell_text(1, 1, some_kanji)\n        self.wait_for_spinner_to_stop()\n\n        page_url = self.browser.current_url\n\n        # * He clicks on a button that clearly allows him to export CSV data.\n        self.wait_for_element_visibility('id=id_export_button', True)\n\n        # * He clicks on it, and sees a popup dialog with two links\n        self.selenium.click('id=id_export_button')\n        self.wait_for_element_visibility('id=id_export_dialog', True)\n        self.wait_for_element_visibility('id=id_export_csv_excel_version', True)\n        self.wait_for_element_visibility('id=id_export_csv_unicode_version', True)\n\n        # * He likes excel, so he tries to download the excel version\n        self.click_link('id_export_csv_excel_version')\n\n        # He is taken to an error page, with a helpful message suggesting he\n        # tries again using the international version\n        self.assertEquals(self.browser.title, \"CSV Export Error: Dirigible\")\n        self.assertEquals(\n            self.get_text(\"id=id_server_error_title\"),\n            \"Could not export CSV file\"\n        )\n        error_text = self.get_text(\"id=id_server_error_text\")\n        msg = \"Sorry, your spreadsheet contains characters that cannot be saved in Excel CSV format\"\n        self.assertTrue(msg in error_text)\n        msg = \"Please try again using the international version\"\n        self.assertTrue(msg in error_text)\n\n\n        # * He notes there is a link back to the sheet page\n        self.wait_for_element_visibility('id=id_sheet_link', True)\n\n        # * But he spots a helpful link to the documentation, which he follows\n        self.selenium.click('css=a[href=\"/documentation/import_export.html\"]')\n        self.selenium.wait_for_page_to_load(PAGE_LOAD_TIMEOUT)\n        self.assertTrue(\n                'Importing and Exporting' in self.browser.title\n        )\n\n        # He goes back to his sheet page\n        self.selenium.open(page_url)\n        self.wait_for_grid_to_appear()\n\n        # * And tries his export again\n        self.selenium.click('id=id_export_button')\n\n        # test unicode download\n        download_url = self.selenium.get_attribute('id=id_export_csv_unicode_version@href')\n        download_url = urljoin(self.browser.current_url, download_url)\n\n        opener = urllib2.build_opener()\n        session_cookie = self.selenium.get_cookie_by_name('sessionid')\n        opener.addheaders.append(('Cookie', 'sessionid=%s' % (session_cookie, )))\n        stream = opener.open(download_url)\n        self.assertEquals(stream.info().gettype(), \"text/csv\")\n        sheet_name = 'Sheet %s' % (sheet_id,)\n        self.assertEquals(\n                stream.info()['Content-Disposition'],\n                'attachment; filename=%s.csv' % (sheet_name,)\n        )\n\n        expected_file_name = path.join(\n                path.dirname(__file__),\n                \"test_data\", \"expected_unicode_csv.csv\"\n        )\n        with open(expected_file_name) as expected_file:\n            self.assertEquals(\n                stream.read().replace(\"\\r\\n\", \"\\n\"),\n                expected_file.read().replace(\"\\r\\n\", \"\\n\")\n            )\n"
  },
  {
    "path": "dirigible/fts/tests/test_2781_FormulaAndFormattedValueMustBeStrings.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom textwrap import dedent\n\nfrom functionaltest import FunctionalTest\n\n\nclass Test_2781_FormulaAndFormattedValueMustBeStrings(FunctionalTest):\n\n    def test_str_formula_and_formatted_value(self):\n        # * Harold wants to be perverse and do strange and unnatural things\n        #   to cells.\n        # * He logs in and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He sets the formula to an object that he made\n        self.enter_usercode(dedent('''\n            class HaroldsObject(object):\n                pass\n\n            worksheet.A1.formula = HaroldsObject()\n        '''))\n\n        # * Dirigible complains.\n        self.wait_for_console_content(\n            'TypeError: cell formula must be str or unicode\\n    User code line 5'\n        )\n\n        # * He tries to molest the formatted_value instead\n        self.enter_usercode(dedent('''\n            class HaroldsObject(object):\n                pass\n\n            worksheet.A1.formatted_value = HaroldsObject()\n        '''))\n\n        self.wait_for_console_content(\n            'TypeError: cell formatted_value must be str or unicode\\n    User code line 5'\n        )\n"
  },
  {
    "path": "dirigible/fts/tests/test_2787_SignUp.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nimport re\nfrom urlparse import urlparse, urljoin\nimport key_codes\n\nfrom browser_settings import SERVER_IP\nfrom functionaltest import FunctionalTest, Url\n\n\nclass Test_2787_SignUp(FunctionalTest):\n\n    def are_password_fields_showing_error(self):\n        return (\n            self.is_element_present('css=#id_password1.error')\n            and self.is_element_present('css=#id_password2.error')\n        )\n\n\n    def test_can_sign_up_from_signup_page(self):\n        # Harold goes to the Dirigible home page\n        self.go_to_url('/')\n\n        # He notes that there are two \"sign up\" links, both pointing to the same URL\n        self.click_link(\"id_signup_link\")\n        signup_url = self.browser.current_url\n        self.go_to_url('/')\n\n        # He follows one of them\n        self.click_link(\"id_signup_call_to_action\")\n        self.assertEquals(self.browser.current_url, signup_url)\n\n        # He notices a \"sign up\" form that requires a username, an email address,\n        # and two copies of the same password.\n        self.assertTrue(self.is_element_present(\n            'css=input#id_username'))\n        self.assertTrue(self.is_element_present(\n            'css=input#id_email'))\n        self.assertTrue(self.is_element_present(\n            'css=input#id_password1'))\n        self.assertTrue(self.is_element_present(\n            'css=input#id_password2'))\n        self.assertTrue(self.is_element_present(\n            'css=input#id_signup_button'))\n        self.assertEquals(\n            self.selenium.get_attribute(\n                'css=#id_signup_button@value'),\n            'Sign up')\n        self.assertEquals(\n            self.selenium.get_attribute(\n                'css=input#id_signup_button@type'),\n            'submit')\n\n        # Being an awkward sod, he tries to sign up with no details.\n        self.click_link('id_signup_button')\n\n        # He is told off.\n        self.assertEquals(\n            self.get_text('id=id_username_error'),\n            \"Please enter a username.\"\n        )\n        self.assertEquals(\n            self.get_text('id=id_email_error'),\n            \"Please enter your email address.\"\n        )\n        self.assertEquals(\n            self.get_text('id=id_password1_error'),\n            \"Please enter a password.\"\n        )\n        self.assertEquals(\n            self.get_text('id=id_password2_error'),\n            \"Please enter a password.\"\n        )\n\n        # He tries again, this time using his friend's username,\n        # but entering sensible details for everything else.\n        username = self.get_my_username() + \"_x\"\n        duplicate_username = self.get_my_username()\n        self.email_address = 'harold.testuser-%s@resolversystems.com' % (self.get_my_username(),)\n        password = 'p4ssw0rd'\n        self.selenium.type(\n            'id=id_username',\n            duplicate_username)\n        self.selenium.type(\n            'id=id_email',\n            self.email_address)\n        self.selenium.type(\n            'id=id_password1',\n            password)\n        self.selenium.type(\n            'id=id_password2',\n            password)\n        self.click_link('id_signup_button')\n\n        # He is told off.\n        self.assertEquals(\n            self.get_text('id=id_username_error'),\n            \"This username is already taken. Please choose another.\"\n        )\n\n        # He tries again with a unique username but mistypes the email address\n        self.selenium.type(\n            'id=id_username',\n            username)\n        self.selenium.type(\n            'id=id_email',\n            '@@@@@')\n        self.selenium.type(\n            'id=id_password1',\n            password)\n        self.selenium.type(\n            'id=id_password2',\n            password)\n        self.click_link('id_signup_button')\n\n        # He is told off.\n        self.assertEquals(\n            self.get_text('id=id_email_error'),\n            \"Please enter a valid email address.\"\n        )\n\n        # He tries again with a unique username but mistypes the password\n        self.selenium.type(\n            'id=id_username',\n            username)\n        self.selenium.type(\n            'id=id_email',\n            self.email_address)\n        self.selenium.type(\n            'id=id_password1',\n            password)\n        self.selenium.type(\n            'id=id_password2',\n            \"hello\")\n        ## Do the last character using native keypresses to make sure that\n        ## all of our client-side validation JS really gets called\n        self.selenium.focus('id=id_password2')\n        self.human_key_press(key_codes.NUMBER_1)\n\n        # Even before he submits the form, the page is grumbling at him\n        self.wait_for(\n            self.are_password_fields_showing_error,\n            lambda : \"Password error to appear\"\n        )\n\n        # With misplaced self-confidence, he goes ahead and clicks the button\n        self.click_link('id_signup_button')\n\n        # He is told off.\n        self.assertEquals(\n            self.get_text('id=id_non_field_errors'),\n            \"You must type the same password each time\"\n        )\n\n        # He finally does it correctly\n        self.selenium.type(\n            'id=id_username',\n            username)\n        self.selenium.type(\n            'id=id_email',\n            self.email_address)\n        self.selenium.type(\n            'id=id_password1',\n            password)\n        self.selenium.type(\n            'id=id_password2',\n            password)\n\n        # Before he clicks the link, he confirms that there is no error in the password fields\n        self.wait_for(\n            lambda : not self.are_password_fields_showing_error(),\n            lambda : \"Password errors to not be there\"\n        )\n\n        self.click_link('id_signup_button')\n\n        # He gets a message saying \"Thank you\" that tells him that an email has been\n        # sent to his address.\n        self.assertTrue('Thank you' in self.selenium.get_body_text())\n        self.assertTrue(self.email_address in self.selenium.get_body_text())\n\n        # There is a link to the Dirigible home page, which he follows and discovers\n        # that it works.\n        self.click_link('id_link_home')\n        self.assertEquals(self.browser.current_url, Url.ROOT)\n\n        # He checks his email, and after a short wait finds a message\n        # from the Dirigible server, that looks like the following string:\n        email_from, email_to, subject, message = self.pop_email_for_client(self.email_address)\n        self.assertEquals(email_to, self.email_address)\n        self.assertEquals(email_from, 'support@projectdirigible.com')\n        self.assertEquals(subject, 'Dirigible Beta Sign-up')\n        self.assertTrue('Click on the following link' in message)\n        confirm_url_re = re.compile(\n            r'<(http://projectdirigible\\.com/signup/activate/[^>]+)>')\n        match = confirm_url_re.search(message)\n        self.assertTrue(match)\n        confirmation_url = match.group(1).replace('projectdirigible.com', SERVER_IP)\n\n        # He decides to type the confirmation link manually into his browser and,\n        # inevitably, gets it completely wrong\n        self.go_to_url(urljoin(Url.ROOT, '/signup/activate/wibble'))\n\n        # He's given a kindly warning.\n        self.assertTrue('the activation link you used was not recognised' in self.selenium.get_body_text())\n\n        # He clicks on the link in the email instead\n        self.go_to_url(confirmation_url)\n\n        body_text = self.selenium.get_body_text()\n        # He's taken to a page that welcomes him aboard and allows him to log in.\n        self.assertTrue(\n            'Welcome to Dirigible' in body_text,\n            'could not find \"Welcome to Dirigible\" on page.  URL:<%s>, body text:\\n%s' % (confirmation_url, body_text[:-100])\n        )\n\n        # He logs in, using the fields on the page.\n        self.login(username, password, already_on_login_page=True)\n\n        # He is taken to his dashboard\n        self.assertEquals(self.browser.title, \"%s's Dashboard: Dirigible\" % (username,))\n        _, __, path, ___, ____, _____ = urlparse(self.browser.current_url)\n        self.assertEquals(path, '/')\n\n        # He's super keen to get in on the Dirigible action, so when he sees the\n        # link saying \"Create new sheet\", he clicks it with gusto\n        self.click_link('id_create_new_sheet')\n\n        # He sees a dialog box promoting the tutorial\n        self.wait_for_element_visibility('id_tutorial_promo_dialog', True)\n        dialog_text = self.get_text('id=id_tutorial_promo_dialog')\n        self.assertTrue('tutorial' in dialog_text.lower())\n\n        # He notes that even when the spinner stops, the focus stays on the dialog's OK\n        # button\n        self.wait_for_spinner_to_stop()\n        self.assertTrue(\n                self.is_element_focused('css=#id_tutorial_promo_dialog_close')\n        )\n\n        # He notices a link to the tutorial inside the dialog\n        tutorial_link_inside_dialog_locator = 'css=#id_tutorial_promo_dialog a#id_tutorial_link'\n        self.wait_for_element_to_appear(tutorial_link_inside_dialog_locator)\n        tutorial_link_url = self.selenium.get_attribute('%s@href' % (tutorial_link_inside_dialog_locator))\n\n        # He clicks the OK button to dismiss the dialog\n        self.selenium.click('id=id_tutorial_promo_dialog_close')\n\n        # the dialog disappears\n        self.wait_for_element_visibility('id=id_tutorial_promo_dialog', False)\n\n        # he goes to the tutorial url he remembers from earlier\n        self.go_to_url(tutorial_link_url)\n\n        # He finds himself on a page which contains the first tutorial\n        expected_title = 'Tutorial part 1: First steps, adding Python to a spreadsheet'\n        self.assertTrue(expected_title in self.browser.title)\n\n        # He goes back to the dashboard\n        self.go_to_url(Url.ROOT)\n\n        # He creates another sheet, ready to get annoyed if he sees that dialog again...\n        self.click_link('id_create_new_sheet')\n\n        # And is happy that it isn't there,\n        self.wait_for_grid_to_appear()\n        self.assertFalse(\n                self.is_element_present('id=id_tutorial_promo_dialog')\n        )\n\n        # He logs out\n        self.logout()\n\n        # He decides that he enjoyed confirming his account so much, he'd like to\n        # do it again.\n        self.go_to_url(confirmation_url)\n\n        # He's given a kindly warning.\n        self.assertTrue('your account might already be activated' in self.selenium.get_body_text())\n\n        # When he returns to his email app, he sees a second email from us,\n        email_from, email_to, subject, message = self.pop_email_for_client(self.email_address)\n        self.assertEquals(email_to, self.email_address)\n        self.assertEquals(email_from, 'support@projectdirigible.com')\n        self.assertEquals(subject, 'Welcome to Dirigible')\n\n        # pointing him towards the tutorial.\n        self.assertTrue('tutorial' in message.lower())\n        self.assertTrue('/documentation/tutorial01.html' in message)\n\n        # We also recommend that he subscribe to the Dirigible blog\n        self.assertTrue('blog.projectdirigible.com' in message)\n\n        # or follows us on Twitter\n        self.assertTrue('twitter.com/dirigiblegrid' in message)\n\n        # Satisfied, he goes back to sleep.\n        self.assertTrue('sleep')\n\n"
  },
  {
    "path": "dirigible/fts/tests/test_2789_ErrorConsoleHTMLEscape.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom functionaltest import FunctionalTest\n\n\nclass Test_2789_ErrorConsoleHTMLEscape(FunctionalTest):\n\n    def test_error_console_output_is_escaped(self):\n        # Harold logs in to Dirigible and creates a new sheet and puts some super-sekrit data in it.\n        self.login_and_create_new_sheet()\n\n        # * He enters usercode that writes HTML to the error console\n        self.append_usercode(\"print '<b>not bold</b>'\")\n\n        # * and notes that the result is correctly escaped\n        self.wait_for_console_content(\"<b>not bold</b>\")\n"
  },
  {
    "path": "dirigible/fts/tests/test_2795_Rewrite_Formulae_during_Cut_and_Paste.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom functionaltest import FunctionalTest\n\n\nclass Test_2795_Rewrite_Formulae_during_Cut_and_Paste(FunctionalTest):\n\n    def test_cut_cell_reference_to_cut_cell_is_rewritten(self):\n        # * Harold logs in to dirigible, his favourite cloud computing tool\n        self.login_and_create_new_sheet()\n\n        # * He writes a formula in A1 that refers to cell A2\n        self.enter_cell_text(1, 1, '=A2')\n        self.wait_for_spinner_to_stop()\n        self.wait_for_cell_to_contain_formula(1, 1, '=A2')\n\n        # * He uses cut & paste to move the formula (and its depenency)\n        #   from A1 to C2\n        self.cut_range((1, 1), (1, 2))\n        self.paste_range((3, 2))\n        self.wait_for_spinner_to_stop()\n\n        # * He is please to see the formula is sensibly rewritten\n        self.wait_for_cell_to_contain_formula(3, 2, '=C3')\n\n\n    def test_cut_cell_reference_to_uncut_cell_is_not_rewritten(self):\n        # * Harold logs in to dirigible, his favourite cloud computing tool\n        self.login_and_create_new_sheet()\n\n        # * He writes a formula in A1 that refers to cell B3\n        self.enter_cell_text(1, 1, '=B3')\n        self.wait_for_spinner_to_stop()\n        self.wait_for_cell_to_contain_formula(1, 1, '=B3')\n\n        # * He uses cut & paste to move the formula from A1 to C2\n        self.cut_range((1, 1), (1, 1))\n        self.paste_range((3, 2))\n        self.wait_for_spinner_to_stop()\n\n        # * He is please to see the formula remains unchanged\n        self.wait_for_cell_to_contain_formula(3, 2, '=B3')\n\n\n    def test_copied_cell_reference_to_copied_cell_is_rewritten(self):\n        # * Harold logs in to dirigible, his favourite cloud computing tool\n        self.login_and_create_new_sheet()\n\n        # * He writes a formula in A1 that refers to cell A2\n        self.enter_cell_text(1, 1, '=A2')\n        self.wait_for_spinner_to_stop()\n        self.wait_for_cell_to_contain_formula(1, 1, '=A2')\n\n        # * He uses copy & paste to move the formula (and its depenency)\n        #   from A1 to C2\n        self.copy_range((1, 1), (1, 2))\n        self.paste_range((3, 2))\n        self.wait_for_spinner_to_stop()\n\n        # * He is please to see the formula is sensibly rewritten\n        self.wait_for_cell_to_contain_formula(3, 2, '=C3')\n\n\n    def test_copied_cell_reference_to_uncopied_cell_is_rewritten(self):\n        # * Harold logs in to dirigible, his favourite cloud computing tool\n        self.login_and_create_new_sheet()\n\n        # * He writes a formula in A1 that refers to cell B3\n        self.enter_cell_text(1, 1, '=B3')\n        self.wait_for_spinner_to_stop()\n        self.wait_for_cell_to_contain_formula(1, 1, '=B3')\n\n        # * He uses copy & paste to move the formula from A1 to C2\n        self.copy_range((1, 1), (1, 1))\n        self.paste_range((3, 2))\n        self.wait_for_spinner_to_stop()\n\n        # * He is please to see the formula is sensibly rewritten\n        self.wait_for_cell_to_contain_formula(3, 2, '=D4')\n\n\n    def test_cut_cellrange_reference_to_completely_cut_cellrange_is_rewritten(self):\n        # * Harold logs in to dirigible, his favourite cloud computing tool\n        self.login_and_create_new_sheet()\n\n        # * Harold creates a formula in A1, referencing A2:A3\n        self.enter_cell_text(1, 1, '=A2:A3')\n        self.wait_for_cell_to_contain_formula(1, 1, '=A2:A3')\n\n        # * He uses cut & paste to move the formula, but not its dependencies,\n        #   2 along and 1 down\n        self.cut_range((1, 1), (1, 3))\n        self.paste_range((3, 2))\n        self.wait_for_spinner_to_stop()\n\n        # * He is please to see the formula is sensibly rewritten\n        self.wait_for_cell_to_contain_formula(3, 2, '=C3:C4')\n\n\n    def test_cut_cellrange_reference_to_partially_cut_cellrange_is_not_rewritten(self):\n        # * Harold logs in to dirigible, his favourite cloud computing tool\n        self.login_and_create_new_sheet()\n\n        # * Harold creates a formula in A1, referencing A2:A3\n        self.enter_cell_text(1, 1, '=A2:A3')\n        self.wait_for_cell_to_contain_formula(1, 1, '=A2:A3')\n\n        # * He uses cut & paste to move the formula, but not its dependencies,\n        #   2 along and 1 down\n        self.cut_range((1, 1), (1, 2))\n        self.paste_range((3, 2))\n        self.wait_for_spinner_to_stop()\n\n        # * He is please to see the formula is not rewritten\n        self.wait_for_cell_to_contain_formula(3, 2, '=A2:A3')\n\n\n    def test_cut_cellrange_reference_to_uncut_cellrange_is_not_rewritten(self):\n        # * Harold logs in to dirigible, his favourite cloud computing tool\n        self.login_and_create_new_sheet()\n\n        # * Harold creates a formula in A1, referencing A2:A3\n        self.enter_cell_text(1, 1, '=A2:A3')\n        self.wait_for_cell_to_contain_formula(1, 1, '=A2:A3')\n\n        # * He uses cut & paste to move the formula, but not its dependencies,\n        #   2 along and 1 down\n        self.cut_range((1, 1), (1, 1))\n        self.paste_range((3, 2))\n        self.wait_for_spinner_to_stop()\n\n        # * He is please to see the formula is not rewritten\n        self.wait_for_cell_to_contain_formula(3, 2, '=A2:A3')\n\n\n    def test_copied_cellrange_reference_to_completely_copied_cellrange_is_rewritten(self):\n        # * Harold logs in to dirigible, his favourite cloud computing tool\n        self.login_and_create_new_sheet()\n\n        # * Harold creates a formula in A1, referencing A2:A3\n        self.enter_cell_text(1, 1, '=A2:A3')\n        self.wait_for_cell_to_contain_formula(1, 1, '=A2:A3')\n\n        # * He uses copy & paste to move the formula, but not its dependencies,\n        #   2 along and 1 down\n        self.copy_range((1, 1), (1, 3))\n        self.paste_range((3, 2))\n        self.wait_for_spinner_to_stop()\n\n        # * He is please to see the formula is sensibly rewritten\n        self.wait_for_cell_to_contain_formula(3, 2, '=C3:C4')\n\n\n    def test_copied_cellrange_reference_to_partially_copied_cellrange_is_rewritten(self):\n        # * Harold logs in to dirigible, his favourite cloud computing tool\n        self.login_and_create_new_sheet()\n\n        # * Harold creates a formula in A1, referencing A2:A3\n        self.enter_cell_text(1, 1, '=A2:A3')\n        self.wait_for_cell_to_contain_formula(1, 1, '=A2:A3')\n\n        # * He uses copy & paste to move the formula, but not its dependencies,\n        #   2 along and 1 down\n        self.copy_range((1, 1), (1, 2))\n        self.paste_range((3, 2))\n        self.wait_for_spinner_to_stop()\n\n        # * He is please to see the formula is sensibly rewritten\n        self.wait_for_cell_to_contain_formula(3, 2, '=C3:C4')\n\n\n    def test_copied_cellrange_reference_to_uncopied_cellrange_is_rewritten(self):\n        # * Harold logs in to dirigible, his favourite cloud computing tool\n        self.login_and_create_new_sheet()\n\n        # * Harold creates a formula in A1, referencing A2:A3\n        self.enter_cell_text(1, 1, '=A2:A3')\n        self.wait_for_cell_to_contain_formula(1, 1, '=A2:A3')\n\n        # * He uses copy & paste to move the formula, but not its dependencies,\n        #   2 along and 1 down\n        self.copy_range((1, 1), (1, 1))\n        self.paste_range((3, 2))\n        self.wait_for_spinner_to_stop()\n\n        # * He is please to see the formula is sensibly rewritten\n        self.wait_for_cell_to_contain_formula(3, 2, '=C3:C4')\n\n\n    def test_outside_cell_references_to_range_cut_from_ARE_rewritten(self):\n        # * Harold logs in to dirigible, his favourite cloud computing tool\n        self.login_and_create_new_sheet()\n\n        # * Harold creates a formula in A1, referencing B2\n        self.enter_cell_text(1, 1, '=B2')\n        self.wait_for_cell_to_contain_formula(1, 1, '=B2')\n\n        # * He uses cut & paste to move the cell B2,\n        # which A1 used to point to\n        # to position C3\n        self.cut_range((2, 2), (2, 2))\n        self.paste_range((3, 3))\n        self.wait_for_spinner_to_stop()\n\n        # * He is pleased to see the formula in A1\n        # now points to the new location\n        self.wait_for_cell_to_contain_formula(1, 1, '=C3')\n\n\n    def test_outside_cell_ranges_pointing_inside_range_cut_from_ARE_rewritten(self):\n        # * Harold logs in to dirigible, his favourite cloud computing tool\n        self.login_and_create_new_sheet()\n\n        # * Harold creates a formula in A1, referencing B2:C3\n        self.enter_cell_text(1, 1, '=B2:C3')\n        self.wait_for_cell_to_contain_formula(1, 1, '=B2:C3')\n\n        # * He uses cut & paste to move the B2:B3\n        # which A1 used to point to\n        # 3 across and 4 down\n        self.cut_range((2, 2), (4, 4))\n        self.paste_range((5, 6))\n        self.wait_for_spinner_to_stop()\n\n        # * He is pleased to see the formula in A1\n        # now points to the new location\n        self.wait_for_cell_to_contain_formula(1, 1, '=E6:F7')\n\n\n    def test_absolute_references_in_copied_range_to_outside_are_not_rewritten(self):\n        # * Harold logs in to dirigible, his favourite cloud computing tool\n        self.login_and_create_new_sheet()\n\n        # * Harold creates a formula in A2, referencing $A$1\n        self.enter_cell_text(1, 1, '1')\n        self.enter_cell_text(1, 2, '=$A$1+1')\n        self.wait_for_cell_to_contain_formula(1, 2, '=$A$1+1')\n\n        # * Copies and pastes A2 to B2\n        self.copy_range((1, 2), (1, 2))\n        self.paste_range((2, 2))\n        self.wait_for_spinner_to_stop()\n\n        # * He is pleased to see the formula in B2 is the same as the one\n        #   in A2.\n        self.wait_for_cell_to_contain_formula(2, 2, '=$A$1+1')\n\n\n    def test_absolute_references_in_copied_range_to_inside_are_not_rewritten(self):\n        # * Harold logs in to dirigible, his favourite cloud computing tool\n        self.login_and_create_new_sheet()\n\n        # * Harold creates a formula in A2, referencing $A$1\n        self.enter_cell_text(1, 1, '1')\n        self.enter_cell_text(1, 2, '=$A$1+1')\n        self.wait_for_cell_to_contain_formula(1, 2, '=$A$1+1')\n\n        # * Copies and pastes A1:A2 to B1:B2\n        self.copy_range((1, 1), (1, 2))\n        self.paste_range((2, 1))\n        self.wait_for_spinner_to_stop()\n\n        # * He is pleased to see the formula in B2 is the same as the one\n        #   in A2.\n        self.wait_for_cell_to_contain_formula(2, 2, '=$A$1+1')\n\n\n    def test_absolute_references_outside_copied_range_to_inside_are_not_rewritten(self):\n        # * Harold logs in to dirigible, his favourite cloud computing tool\n        self.login_and_create_new_sheet()\n\n        # * Harold creates a formula in A2, referencing $A$1\n        self.enter_cell_text(1, 1, '1')\n        self.enter_cell_text(1, 2, '=$A$1+1')\n        self.wait_for_cell_to_contain_formula(1, 2, '=$A$1+1')\n\n        # * Copies and pastes A1 to B1\n        self.copy_range((1, 1), (1, 1))\n        self.paste_range((2, 1))\n        self.wait_for_spinner_to_stop()\n\n        # * He is pleased to see the formula in A2 is unchanged\n        self.wait_for_cell_to_contain_formula(1, 2, '=$A$1+1')\n\n\n    def test_absolute_references_in_cut_range_to_outside_are_not_rewritten(self):\n        # * Harold logs in to dirigible, his favourite cloud computing tool\n        self.login_and_create_new_sheet()\n\n        # * Harold creates a formula in A2, referencing $A$1\n        self.enter_cell_text(1, 1, '1')\n        self.enter_cell_text(1, 2, '=$A$1+1')\n        self.wait_for_cell_to_contain_formula(1, 2, '=$A$1+1')\n\n        # * Cut and pastes A2 to B2\n        self.cut_range((1, 2), (1, 2))\n        self.paste_range((2, 2))\n        self.wait_for_spinner_to_stop()\n\n        # * He is pleased to see the formula in B2 is the same as the one\n        #   in A2.\n        self.wait_for_cell_to_contain_formula(2, 2, '=$A$1+1')\n\n\n    def test_absolute_references_in_cut_range_to_inside_are_rewritten(self):\n        # * Harold logs in to dirigible, his favourite cloud computing tool\n        self.login_and_create_new_sheet()\n\n        # * Harold creates a formula in A2, referencing $A$1\n        self.enter_cell_text(1, 1, '1')\n        self.enter_cell_text(1, 2, '=$A$1+1')\n        self.wait_for_cell_to_contain_formula(1, 2, '=$A$1+1')\n\n        # * Cut and pastes A1:A2 to B1:B2\n        self.cut_range((1, 1), (1, 2))\n        self.paste_range((2, 1))\n        self.wait_for_spinner_to_stop()\n\n        # * He is pleased to see the formula in B2 is properly rewritten\n        self.wait_for_cell_to_contain_formula(2, 2, '=$B$1+1')\n\n\n    def test_absolute_references_outside_cut_range_to_inside_are_rewritten(self):\n        # * Harold logs in to dirigible, his favourite cloud computing tool\n        self.login_and_create_new_sheet()\n\n        # * Harold creates a formula in A2, referencing $A$1\n        self.enter_cell_text(1, 1, '1')\n        self.enter_cell_text(1, 2, '=$A$1+1')\n        self.wait_for_cell_to_contain_formula(1, 2, '=$A$1+1')\n\n        # * Cut and pastes A1 to B1\n        self.cut_range((1, 1), (1, 1))\n        self.paste_range((2, 1))\n        self.wait_for_spinner_to_stop()\n\n        # * He is pleased to see the formula in B2 is rewritten\n        self.wait_for_cell_to_contain_formula(1, 2, '=$B$1+1')\n\n\n    def test_column_absolute_references_are_partially_rewritten(self):\n        # * Harold logs in to dirigible, his favourite cloud computing tool\n        self.login_and_create_new_sheet()\n\n        # * Harold creates a formula in A2, referencing $A1\n        self.enter_cell_text(1, 1, '1')\n        self.enter_cell_text(1, 2, '=$A1+1')\n        self.wait_for_cell_to_contain_formula(1, 2, '=$A1+1')\n\n        # * Copies and pastes A2 to B3\n        self.copy_range((1, 2), (1, 2))\n        self.paste_range((2, 3))\n        self.wait_for_spinner_to_stop()\n\n        # * He is pleased to see the formula in B3 has been rewritten to\n        #   refer to $A2\n        self.wait_for_cell_to_contain_formula(2, 3, '=$A2+1')\n\n\n    def test_row_absolute_references_are_partially_rewritten(self):\n        # * Harold logs in to dirigible, his favourite cloud computing tool\n        self.login_and_create_new_sheet()\n\n        # * Harold creates a formula in A2, referencing A$1\n        self.enter_cell_text(1, 1, '1')\n        self.enter_cell_text(1, 2, '=A$1+1')\n        self.wait_for_cell_to_contain_formula(1, 2, '=A$1+1')\n\n        # * Copies and pastes A2 to B3\n        self.copy_range((1, 2), (1, 2))\n        self.paste_range((2, 3))\n        self.wait_for_spinner_to_stop()\n\n        # * He is pleased to see the formula in B3 has been rewritten to\n        #   refer to B$1.\n        self.wait_for_cell_to_contain_formula(2, 3, '=B$1+1')\n\n\n    def test_cell_rewrites_that_go_off_grid_are_handled(self):\n        # * Harold logs in to dirigible, his favourite cloud computing tool\n        self.login_and_create_new_sheet()\n\n        # * Harold creates a formula in A2, referencing A1\n        self.enter_cell_text(1, 1, '1')\n        self.enter_cell_text(1, 2, '=A1+1')\n        self.wait_for_cell_to_contain_formula(1, 2, '=A1+1')\n\n        # * Copies and pastes A2 to B1\n        self.copy_range((1, 2), (1, 2))\n        self.paste_range((2, 1))\n        self.wait_for_spinner_to_stop()\n\n        # * He is pleased to see the formula in B1\n        #   has an error message\n        self.wait_for_cell_to_contain_formula(2, 1, '=#Invalid!+1')\n\n\n    def test_cellrange_rewrites_that_go_off_grid_are_handled(self):\n        # * Harold logs in to dirigible, his favourite cloud computing tool\n        self.login_and_create_new_sheet()\n\n        # * Harold creates a formula in A3, referencing A1:A2\n        self.enter_cell_text(1, 1, '1')\n        self.enter_cell_text(1, 2, '2')\n        self.enter_cell_text(1, 3, '=sum(A1:A2)')\n        self.wait_for_cell_to_contain_formula(1, 3, '=sum(A1:A2)')\n\n        # * Copies and pastes A3 to B2\n        self.copy_range((1, 3), (1, 3))\n        self.paste_range((2, 2))\n        self.wait_for_spinner_to_stop()\n\n        # * He is pleased to see the formula in B2\n        #   has an error message\n        self.wait_for_cell_to_contain_formula(2, 2, '=sum(#Invalid!:B1)')\n\n\n    def test_cut_paste_paste_rewrites_formulae_for_2nd_paste(self):\n        # * Harold logs in to dirigible, his favourite cloud computing tool\n        self.login_and_create_new_sheet()\n\n        # * Harold creates a formula in A1\n        self.enter_cell_text(1, 1, '=B1')\n\n        # He pastes elsewhere, then pastes again\n        self.cut_range((1, 1), (1, 1))\n        self.paste_range((1, 2))\n        self.paste_range((1, 3))\n        self.wait_for_spinner_to_stop()\n\n        # * the first paste (from a cut) does not rewrite the formula\n        self.wait_for_cell_to_contain_formula(1, 2, '=B1')\n        # * the second paste should act like a copy from the first paste\n        self.wait_for_cell_to_contain_formula(1, 3, '=B2')\n\n\n    def test_copy_paste_paste_always_rewrites_formulae(self):\n        # * Harold logs in to dirigible, his favourite cloud computing tool\n        self.login_and_create_new_sheet()\n\n        # * Harold creates a formula in A1\n        self.enter_cell_text(1, 1, '=B1')\n\n        # He pastes elsewhere, then pastes again\n        self.copy_range((1, 1), (1, 1))\n        self.paste_range((1, 2))\n        self.paste_range((1, 3))\n        self.wait_for_spinner_to_stop()\n\n        # * the first paste (from a copy) does rewrite the formula\n        self.wait_for_cell_to_contain_formula(1, 2, '=B2')\n        # * the second paste should act like a copy from the first paste\n        self.wait_for_cell_to_contain_formula(1, 3, '=B3')\n\n\n    def test_buttons(self):\n        pass # *TODO we need cut, copy and paste buttons\n\n"
  },
  {
    "path": "dirigible/fts/tests/test_2799_FillDownDuringPaste.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom functionaltest import FunctionalTest\nimport key_codes\n\n\nclass Test_2799_fill_down_during_paste(FunctionalTest):\n\n    def wait_for_cell_to_contain_formula(self, column, row, formula):\n        self.open_cell_for_editing(column, row)\n        self.wait_for_cell_editor_content(formula)\n        self.human_key_press(key_codes.ENTER)\n\n\n    def test_fill_down_formula_with_copy_and_paste(self):\n        # * Harold logs in to dirigible, his favourite cloud computing tool\n        self.login_and_create_new_sheet()\n\n        # * Harold populates a table full of data\n        self.enter_cell_text(1, 1, '1')\n        self.enter_cell_text(2, 1, '1')\n        self.enter_cell_text(1, 2, '2')\n        self.enter_cell_text(2, 2, '2')\n        self.enter_cell_text(1, 3, '3')\n        self.enter_cell_text(2, 3, '3')\n\n        # * He writes a function to sum the values in a row\n        self.enter_cell_text(3, 1, '=A1+B1')\n\n        # * He uses copy & paste to 'fill down' his formula\n        self.copy_range((3, 1), (3, 1))\n        self.paste_range((3, 2), (3, 3))\n\n        self.wait_for_cell_to_contain_formula(3, 2, '=A2+B2')\n        self.wait_for_cell_to_contain_formula(3, 3, '=A3+B3')\n\n\n    def test_fill_down_2x2_clipboard_into_3x5_selection(self):\n        # * Harold logs in to dirigible, his favourite cloud computing tool\n        self.login_and_create_new_sheet()\n\n        # * Harold populates a table full of data\n        self.enter_cell_text(1, 1, '=C1')\n        self.enter_cell_text(2, 1, '=D1')\n        self.enter_cell_text(1, 2, '=C2')\n        self.enter_cell_text(2, 2, '=D2')\n\n        # * He uses copy & paste to 'fill down' his formula\n        self.copy_range((1, 1), (2, 2))\n        self.paste_range((3, 3), (5, 7))\n\n        self.wait_for_cell_to_contain_formula(3, 3, '=E3')\n        self.wait_for_cell_to_contain_formula(4, 3, '=F3')\n        self.wait_for_cell_to_contain_formula(5, 3, '=G3')\n\n        self.wait_for_cell_to_contain_formula(3, 4, '=E4')\n        self.wait_for_cell_to_contain_formula(4, 4, '=F4')\n        self.wait_for_cell_to_contain_formula(5, 4, '=G4')\n        \n        self.wait_for_cell_to_contain_formula(3, 5, '=E5')\n        self.wait_for_cell_to_contain_formula(4, 5, '=F5')\n        self.wait_for_cell_to_contain_formula(5, 5, '=G5')\n        \n        self.wait_for_cell_to_contain_formula(3, 6, '=E6')\n        self.wait_for_cell_to_contain_formula(4, 6, '=F6')\n        self.wait_for_cell_to_contain_formula(5, 6, '=G6')\n        \n        self.wait_for_cell_to_contain_formula(3, 7, '=E7')\n        self.wait_for_cell_to_contain_formula(4, 7, '=F7')\n        self.wait_for_cell_to_contain_formula(5, 7, '=G7')\n\n        self.wait_for_cell_to_contain_formula(3, 8, '')\n        self.wait_for_cell_to_contain_formula(4, 8, '')\n        self.wait_for_cell_to_contain_formula(5, 8, '')\n\n        self.wait_for_cell_to_contain_formula(6, 3, '')\n        self.wait_for_cell_to_contain_formula(6, 4, '')\n        self.wait_for_cell_to_contain_formula(6, 5, '')\n        self.wait_for_cell_to_contain_formula(6, 6, '')\n        self.wait_for_cell_to_contain_formula(6, 7, '')\n\n"
  },
  {
    "path": "dirigible/fts/tests/test_2812_CutCopyPasteInEditMode.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom __future__ import with_statement\n\ntry:\n    import unittest2 as unittest\nexcept ImportError:\n    import unittest\n\nfrom functionaltest import FunctionalTest, humanesque_delay\nimport key_codes\n\n\nclass Test_2812_CutCopyPasteInEditMode(FunctionalTest):\n\n    def test_delete_key_while_editing_still_does_what_it_should(self):\n        # * Harold logs in and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He enters three characters in A1\n        self.open_cell_for_editing(1, 1)\n        self.human_key_press(key_codes.NUMBER_1)\n        self.human_key_press(key_codes.NUMBER_2)\n        self.human_key_press(key_codes.NUMBER_3)\n\n        # * He hits ctrl-A\n\twith self.key_down(key_codes.CTRL):\n            self.human_key_press(key_codes.LETTER_A)\n\n        # He hits control-X\n\twith self.key_down(key_codes.CTRL):\n            self.human_key_press(key_codes.LETTER_X)\n\n        # the text is gone\n        self.wait_for_cell_editor_content('')\n\n        # He hits control-V\n\twith self.key_down(key_codes.CTRL):\n            self.human_key_press(key_codes.LETTER_V)\n\n\t# His cut text returns\n        self.wait_for_cell_editor_content('123')\n\n        # * He selects everything again\n\twith self.key_down(key_codes.CTRL):\n            self.human_key_press(key_codes.LETTER_A)\n\n        # He hits control-C\n\twith self.key_down(key_codes.CTRL):\n            self.human_key_press(key_codes.LETTER_C)\n\n        # Nothing happens to his carefully-crafted text\n        self.wait_for_cell_editor_content('123')\n\n        # He hits control-V twice\n\twith self.key_down(key_codes.CTRL):\n            self.human_key_press(key_codes.LETTER_V)\n            self.human_key_press(key_codes.LETTER_V)\n\n\t# and is presented with his final masterpiece\n        self.wait_for_cell_editor_content('123123')\n"
  },
  {
    "path": "dirigible/fts/tests/test_2814_PublicWorksheets.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\nfrom __future__ import with_statement\n\nfrom os import path\nimport re\nimport time\nfrom urllib2 import HTTPError\nfrom urlparse import urljoin\n\nfrom functionaltest import FunctionalTest, PAGE_LOAD_TIMEOUT, SERVER_IP\nimport key_codes\n\nclass Test_2814_PublicWorksheets(FunctionalTest):\n    user_count = 3\n    email_address = None\n\n\n    def tearDown(self):\n        FunctionalTest.tearDown(self)\n        if self.email_address:\n            self.clear_email_for_address(self.email_address)\n\n\n    def waitForButtonToIndicateSheetIsPublic(self, public):\n        if public:\n            expected_text = 'Sheet is public'\n        else:\n            expected_text = 'Sheet is private'\n\n        def get_button_title():\n            return self.selenium.get_attribute('css=#id_security_button@title')\n        self.wait_for(\n            lambda : expected_text in get_button_title(),\n            lambda : \"%r to be in %r\" % (expected_text, get_button_title())\n        )\n\n        def get_button_alt_text():\n            return self.selenium.get_attribute('css=#id_security_button@alt')\n        self.wait_for(\n            lambda : expected_text in get_button_alt_text(),\n            lambda : \"%r to be in %r\" % (expected_text, get_button_alt_text())\n        )\n\n\n    def test_public_worksheets_visible_readonly_and_copiable_for_others(self):\n        # * Harold logs in and creates a new sheet\n        sheet_id = self.login_and_create_new_sheet()\n\n        # * He gives the sheet a catchy name\n        self.set_sheet_name('spaceshuttle')\n\n        # * He enters some formulae n stuff\n        self.enter_cell_text(2, 3, '23')\n        self.enter_cell_text(2, 4, '=my_add_function(B3)')\n        self.prepend_usercode('my_add_function = lambda x : x + 2')\n        self.wait_for_cell_value(2, 4, '25')\n\n        # * He notes that the tooltip for the security icon indicates that the\n        # sheet is private\n        self.waitForButtonToIndicateSheetIsPublic(False)\n\n        # * He clicks on the security icon\n        self.selenium.click('id=id_security_button')\n\n        # He sees a tickbox, currently unticked, saying make worksheet public\n        self.wait_for_element_visibility(\n                'id=id_security_form', True)\n        self.wait_for_element_visibility(\n                'id=id_security_form_public_sheet_checkbox', True)\n\n        self.assertEquals(\n            self.selenium.get_value('id=id_security_form_public_sheet_checkbox'),\n            'off'\n        )\n        # He ticks it and dismisses the dialog\n        self.selenium.click('id=id_security_form_public_sheet_checkbox')\n        self.selenium.click('id=id_security_form_ok_button')\n\n        # * He notes that the tooltip for the security icon indicates that the\n        # sheet is public\n        self.waitForButtonToIndicateSheetIsPublic(True)\n\n        # He notes down the URL and emails it to his colleague Harriet\n        harolds_url = self.browser.current_url\n\n        # He logs out\n        self.logout()\n\n        # * Later on, Harriet logs into teh Dirigible and heads on over to\n        #   Harold's spreadsheet\n        self.login(self.get_my_usernames()[1])\n        self.go_to_url(harolds_url)\n\n        # She sees the values n stuff\n        self.wait_for_grid_to_appear()\n        self.wait_for_cell_value(2, 4, '25')\n\n        # * She notices that all toolbar icons are missing,\n        # apart from download-as-csv\n        map(\n            lambda e: self.wait_for_element_presence(e, False),\n            [\n                'id=id_import_button',\n                'id=id_cut_button',\n                'id=id_copy_button',\n                'id=id_paste_button',\n                'id=id_security_button',\n            ]\n        )\n        self.wait_for_element_visibility('id=id_export_button', True)\n\n        # * She tries to edit some formulae, but can't\n        self.selenium.double_click(\n                self.get_cell_locator(1, 1)\n        )\n        self.selenium.focus(\n                self.get_cell_locator(1, 1)\n        )\n        time.sleep(1)\n        self.wait_for_element_presence(\n                self.get_active_cell_editor_locator(),\n                False\n        )\n\n        # * she tries to edit the cell again, using the formula bar, but cannot\n        self.assertEquals(\n            self.selenium.get_attribute(self.get_formula_bar_locator() + '@readonly'),\n            'true'\n        )\n\n        # * She tries to edit some usercode, but can't\n        original_code = self.get_usercode()\n        self.selenium.get_eval('window.editor.focus()')\n        self.human_key_press(key_codes.LETTER_A)\n        time.sleep(1)\n        self.wait_for_usercode_editor_content(original_code)\n\n        # * She tries to edit the sheet name, but can't\n\n        # * mouses over the sheet name and notes that the appearance\n        #   does not change to indicate that it's editable\n        self.selenium.mouse_over('id=id_sheet_name')\n        time.sleep(1)\n        self.wait_for(\n            lambda: self.get_css_property('#id_sheet_name', 'background-color') == 'transparent',\n            lambda: 'ensure sheet name background stays normal')\n\n        # * He clicks on the sheet name, the sheetname edit textarea does\n        #   not appear,\n        self.selenium.click('id=id_sheet_name')\n        time.sleep(1)\n        self.wait_for(\n            lambda: not self.is_element_present('id=edit-id_sheet_name'),\n            lambda: 'ensure editable sheetname does not appear')\n\n        def download_as_csv():\n            self.selenium.click('id=id_export_button')\n            self.wait_for_element_visibility('id=id_export_dialog', True)\n            download_url = self.selenium.get_attribute('id=id_export_csv_excel_version@href')\n            download_url = urljoin(self.browser.current_url, download_url)\n\n            stream = self.get_url_with_session_cookie(download_url)\n            self.assertEquals(stream.info().gettype(), \"text/csv\")\n            self.assertEquals(\n                    stream.info()['Content-Disposition'],\n                    'attachment; filename=spaceshuttle.csv'\n            )\n\n            expected_file_name = path.join(\n                    path.dirname(__file__),\n                    \"test_data\", \"public_sheet_csv_file.csv\"\n            )\n            with open(expected_file_name) as expected_file:\n                self.assertEquals(\n                    stream.read().replace(\"\\r\\n\", \"\\n\"),\n                    expected_file.read().replace(\"\\r\\n\", \"\\n\")\n                )\n\n        # * She confirms that she can download a csv of the sheet\n        download_as_csv()\n\n        # * She uses some l33t haxx0ring skillz to try and send a\n        #   setcellformula Ajax call directly\n        # It doesn't work.\n        with self.assertRaises(HTTPError):\n            response = self.get_url_with_session_cookie(\n                    urljoin(harolds_url, '/set_cell_formula/'),\n                    data={'column':3, 'row': 4, 'formula': '=jeffk'}\n            )\n\n        # * \"Aha!\" she says, as she notices a link allowing her to copy the sheet,\n        self.wait_for_element_visibility('id_copy_sheet_link', True)\n        # which she then clicks\n        self.selenium.click('id=id_copy_sheet_link')\n\n        # She is taken to a sheet of her own\n        self.selenium.wait_for_page_to_load(PAGE_LOAD_TIMEOUT)\n        self.wait_for_grid_to_appear()\n\n        # It looks a lot like Harold's but has a different url\n        harriets_url = self.browser.current_url\n        self.assertFalse(harriets_url == harolds_url)\n        self.wait_for_cell_value(2, 4, '25')\n\n        # And she is able to change cell formulae\n        self.enter_cell_text(2, 3, '123')\n        self.wait_for_cell_value(2, 4, '125')\n\n        # And she is able to change usercode\n        self.append_usercode('worksheet[2, 4].value += 100')\n        self.wait_for_cell_value(2, 4, '225')\n\n        # And she is well pleased. So much so that she emails two\n        # friends about these two sheets (and they tell two\n        # friends, and they tell two friends, and so on, and so\n        # on.  $$$$)\n        self.logout()\n\n        # * Helga is a Dirigible user, but she isn't logged in.\n        #   She goes to Harold's page, and sees that it is good.\n        self.go_to_url(harolds_url)\n        self.wait_for_grid_to_appear()\n        self.wait_for_cell_value(2, 4, '25')\n\n        # She clicks on the big copy button, and is taken to the\n        # login form\n        self.selenium.click('id=id_copy_sheet_link')\n        self.selenium.wait_for_page_to_load(PAGE_LOAD_TIMEOUT)\n        self.wait_for_element_visibility('id_login_form_wrap', True)\n\n        # She logs in, and is taken straight to her new copy of\n        # Harold's sheet\n        self.login(\n                self.get_my_usernames()[2],\n                already_on_login_page=True\n        )\n        self.wait_for_grid_to_appear()\n\n        helgas_url = self.browser.current_url\n        self.assertFalse(helgas_url == harolds_url)\n        self.assertFalse(helgas_url == harriets_url)\n        self.wait_for_cell_value(2, 4, '25')\n\n        # Helga makes some edits, which she considers superior to\n        # Harriet's\n        self.enter_cell_text(2, 3, '1000')\n        self.append_usercode('worksheet[2, 4].value += 1000')\n        self.wait_for_cell_value(2, 4, '2002')\n\n        # Helga now decides to go and see Harriet's sheet, to\n        # laugh at the inferiority of Harriet's fork\n        # Her access is denied.\n        self.assert_HTTP_error(harriets_url, 403)\n\n        # * Harriet's other friend, Hugh, is not a Dirigible user.... yet.\n        # He goes to Harold's sheet and sees that it is good\n        self.logout()\n        self.go_to_url(harolds_url)\n        self.wait_for_grid_to_appear()\n        self.wait_for_cell_value(2, 4, '25')\n\n        # So good that he clicks the copy button too, despite never\n        # having heard of this Dirigible thingy\n        self.selenium.click('id=id_copy_sheet_link')\n        self.selenium.wait_for_page_to_load(PAGE_LOAD_TIMEOUT)\n\n        # He is taken to the login form,\n        self.wait_for_element_visibility('id_login_form_wrap', True)\n\n        # on which he spots a nice friendly link inviting him to register.\n        # It says 'free' and everyfink.\n        self.wait_for_element_to_appear('id=id_login_signup_link')\n        self.wait_for_element_to_appear('id=id_login_signup_blurb')\n        self.assertTrue(\"free\" in self.get_text('id=id_login_signup_blurb'))\n\n        # Hugh goes through the whole registration rigmarole,\n        self.selenium.click('id=id_login_signup_link')\n        self.selenium.wait_for_page_to_load(PAGE_LOAD_TIMEOUT)\n        username = self.get_my_username() + \"_x\"\n        self.email_address = 'harold.testuser-%s@resolversystems.com' % (username,)\n        password = \"p4ssw0rd\"\n        self.selenium.type('id=id_username', username)\n        self.selenium.type('id=id_email', self.email_address)\n        self.selenium.type('id=id_password1', password)\n        self.selenium.type('id=id_password2', password)\n        self.click_link('id_signup_button')\n\n        email_from, email_to, subject, message = self.pop_email_for_client(self.email_address)\n        self.assertEquals(subject, 'Dirigible Beta Sign-up')\n        confirm_url_re = re.compile(\n            r'<(http://projectdirigible\\.com/signup/activate/[^>]+)>'\n        )\n        match = confirm_url_re.search(message)\n        self.assertTrue(match)\n        confirmation_url = match.group(1).replace('projectdirigible.com', SERVER_IP)\n\n        # * Hugh then logs in\n        self.go_to_url(confirmation_url)\n        self.login(username, password, already_on_login_page=True)\n\n        # and has his socks knocked off by the presence of the copy of Harold's\n        # sheet in his dashboard\n        self.selenium.click('link=spaceshuttle')\n\n        # and it has the copied content\n        self.selenium.wait_for_page_to_load(PAGE_LOAD_TIMEOUT)\n        self.wait_for_grid_to_appear()\n        self.wait_for_cell_value(2, 4, '25')\n\n        # Harold logs in and sees that his original sheet is unharmed by all of\n        # the other users editing theirs\n        self.login(self.get_my_usernames()[0])\n        self.go_to_url(harolds_url)\n        self.wait_for_grid_to_appear()\n        self.wait_for_cell_value(2, 4, '25')\n\n\n    def test_admin_can_copy_non_public_sheets(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        sheet_id = self.login_and_create_new_sheet()\n        self.enter_cell_text(1, 1, '=6 + 11')\n\n        # * He is having some trouble working with a complicated task\n        #   so he asks an admin for help. Giving him the sheet url\n        sheet_url = '/user/%s/sheet/%s' % (self.get_my_username(), sheet_id)\n        self.logout()\n\n        # * The admin logs in and visits the sheet\n        self.login('admin', '<KI*7ujm')\n        self.go_to_url(sheet_url)\n\n        # * He copies the sheet to his own account so he can edit it without\n        #   changing Harold's sheet\n        self.selenium.click('id=id_copy_sheet_link')\n        self.selenium.wait_for_page_to_load(PAGE_LOAD_TIMEOUT)\n        self.wait_for_grid_to_appear()\n        self.wait_for_cell_value(1, 1, '17')\n\n\n    def test_link_to_documentation_in_dialog(self):\n        # * Harold logs in to Dirigible and creates a new sheet\n        self.login_and_create_new_sheet()\n\n        # * He goes to the security dialog.\n        self.selenium.click('id=id_security_button')\n        self.wait_for_element_visibility('id=id_security_form', True)\n        self.wait_for_element_visibility('id=id_security_form_save_error', False)\n\n        # * He is puzzled by the public sheet option, so he clicks on its help link\n        # * The link goes into a new window\n        ## Selenium gets Very Unhappy with target=_blank, so we just check that it's there and\n        ## then remove it so as not to frighten the poor thing.\n        self.assertEquals(\n            self.selenium.get_attribute('id=id_security_form_public_sheet_help@target'),\n            \"_blank\"\n        )\n        self.selenium.get_eval(\"window.$('#id_security_form_public_sheet_help').removeAttr('target')\")\n        self.selenium.click(\"id=id_security_form_public_sheet_help\")\n        self.selenium.wait_for_page_to_load(PAGE_LOAD_TIMEOUT)\n\n        # * and is taken to a documentation page that explains it all\n        title = self.browser.title.lower()\n        self.assertTrue('public' in title)\n        self.assertTrue('sheet' in title)\n\n"
  },
  {
    "path": "dirigible/fts/tests/test_2828_GridShouldNotStealFocusOnRecalc.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom textwrap import dedent\n\nimport key_codes\nfrom functionaltest import FunctionalTest, snapshot_on_error\n\n\nclass Test_2828_GridShouldNotStealFocusOnRecalc(FunctionalTest):\n\n    @snapshot_on_error\n    def test_recalc_doesnt_steal_focus_from_usercode(self):\n        # Harold logs on and creates a sheet\n        self.login_and_create_new_sheet()\n\n        # Harold enters some usercode that takes a while to recalc\n        # setting a value in the grid\n        self.enter_usercode(dedent('''\n            import time\n            time.sleep(5)\n        '''))\n\n        # while the recalc is running he types something in the editor\n        #self.selenium.focus('id=id_usercode')\n        self.selenium.get_eval('window.editor.focus()')\n        self.human_key_press(key_codes.LETTER_A)\n\n        # When the recalc comes back, he types some more text, and notes with\n        # satifaction that the grid has not stolen the focus\n        self.wait_for_spinner_to_stop()\n        self.human_key_press(key_codes.LETTER_A)\n\n        self.wait_for(\n            lambda : 'aa' in self.get_usercode(),\n            lambda : \"Usercode editor didn't contain aa::\\n%s\" % (\n                self.get_usercode(),\n            ),\n        )\n\n"
  },
  {
    "path": "dirigible/fts/tests/test_2839_CutCopyPasteButtons.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\r\n# All Rights Reserved\r\n#\r\n\r\nfrom functionaltest import FunctionalTest\r\n\r\n\r\nclass Test_2839_CutCopyPasteButtons(FunctionalTest):\r\n\r\n    def test_buttons_exist_and_are_clickable(self):\r\n        # * Harold logs in to Dirigible and creates a nice shiny new sheet\r\n        self.login_and_create_new_sheet()\r\n\r\n        # * He sees that toolbar buttons exist for cut, copy, paste\r\n        self.wait_for_element_visibility('id=id_cut_button', True)\r\n        self.wait_for_element_visibility('id=id_copy_button', True)\r\n        self.wait_for_element_visibility('id=id_paste_button', True)\r\n\r\n        # * He enters a simple cell value\r\n        self.enter_cell_text(1, 1, \"Sausage\")\r\n\r\n        # * He cuts it using the toolbar button, and it dissapears\r\n        self.click_on_cell(1, 1)\r\n        self.selenium.click('id=id_cut_button')\r\n        self.wait_for_cell_value(1, 1, '')\r\n\r\n        # * He pastes it elsewhere, he confirms it appears there\r\n        self.click_on_cell(2, 1)\r\n        self.selenium.click('id=id_paste_button')\r\n        self.wait_for_cell_value(2, 1, 'Sausage')\r\n\r\n        # * He copies it and pastes elsewhere\r\n        self.click_on_cell(2, 1)\r\n        self.selenium.click('id=id_copy_button')\r\n        self.click_on_cell(3, 1)\r\n        self.selenium.click('id=id_paste_button')\r\n        # * He makes sure the data is visible in both locations\r\n        self.wait_for_cell_value(3, 1, 'Sausage')\r\n        self.wait_for_cell_value(2, 1, 'Sausage')\r\n"
  },
  {
    "path": "dirigible/fts/tests/test_2844_CantMakeRowHeaderActive.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom functionaltest import FunctionalTest\n\n\nclass Test_2844_CantMakeRowHeaderActive(FunctionalTest):\n\n    def test_cant_make_row_header_active(self):\n        ## Of course, this FT will have to go when we can select whole rows/cols.\n\n        # * Harold logs in to Dirigible and creates a nice shiny new sheet\n        self.login_and_create_new_sheet()\n\n        # * He notes that A1 is the active cell\n        self.wait_for_cell_to_become_active(1, 1)\n\n        # * He clicks on the row header (column 0) for row 3.\n        self.click_on_cell(0, 3)\n\n        # * He notes that A1 is still the active cell\n        self.wait_for_cell_to_become_active(1, 1)\n\n        # * He does a small mouse drag within the header (column 0)\n        self.mouse_drag((0, 3), (0, 4))\n\n        # * He notes that A1 is still the active cell\n        self.wait_for_cell_to_become_active(1, 1)\n\n"
  },
  {
    "path": "dirigible/fts/tests/test_2848_WorksheetBounds.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\r\n# All Rights Reserved\r\n#\r\n\r\nfrom functionaltest import FunctionalTest\r\n\r\n\r\nclass Test_2848_WorksheetBounds(FunctionalTest):\r\n\r\n    def test_access_worksheet_bounds(self):\r\n        # * Harold logs in to Dirigible and creates a nice shiny new sheet\r\n        self.login_and_create_new_sheet()\r\n\r\n        # He enters some data\r\n        self.enter_cell_text(4, 2, \"Top right\")\r\n        self.enter_cell_text(2, 10, \"Bottom left\")\r\n\r\n        # He writes some usercode to access the bounds of the worksheet\r\n        self.append_usercode(\"worksheet[3, 5].value = worksheet.bounds\")\r\n        self.append_usercode(\"worksheet[3, 6].value = worksheet.bounds.bottom\")\r\n\r\n        # ...and is delighted to discover it works!\r\n        self.wait_for_cell_value(3, 5, \"(2, 2, 4, 10)\")\r\n        self.wait_for_cell_value(3, 6, \"10\")\r\n"
  },
  {
    "path": "dirigible/fts/tests/test_2862_CopyAndPasteFormulaWithErrors.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nimport time\nfrom textwrap import dedent\n\nimport key_codes\nfrom functionaltest import FunctionalTest, snapshot_on_error\n\n\nclass Test_2892_CopyAndPasteFormulaWithErrors(FunctionalTest):\n\n    @snapshot_on_error\n    def test_recalc_doesnt_steal_focus_from_usercode(self):\n        # Harold logs on and creates a sheet\n        self.login_and_create_new_sheet()\n\n        # Harold enters a formula with a sweary error in it\n        self.enter_cell_text(1, 1, '=:!&@#&**%!')\n        self.wait_for_spinner_to_stop()\n\n        # He then tries to spread the joy by cut & pasting it\n        self.copy_range((1, 1), (1, 1))\n        self.paste_range((1, 2), (1, 2))\n\n        # He sees the invalid formula in both cells now\n\n        self.wait_for_cell_to_contain_formula(1, 1, '=:!&@#&**%!')\n        self.wait_for_cell_to_contain_formula(1, 2, '=:!&@#&**%!')\n"
  },
  {
    "path": "dirigible/fts/tests/test_2872_CellTooltips.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nimport time\nfrom textwrap import dedent\n\nimport key_codes\nfrom functionaltest import FunctionalTest, snapshot_on_error\n\n\nclass Test_2892_CellTooltips(FunctionalTest):\n\n    @snapshot_on_error\n    def test_formulae_are_shown_in_tooltips(self):\n        # Harold logs on and creates a sheet\n        self.login_and_create_new_sheet()\n\n        # Harold enters a formula that won't evaluate\n        self.enter_cell_text(1, 1, '=A2')\n        self.wait_for_spinner_to_stop()\n\n        # He then sees that a tooltip is set on the cell that would show up if\n        # he hovered over it\n\n        tooltip = self.selenium.get_attribute(\n                self.get_cell_shown_formula_locator(1, 1) + '@title'\n        )\n\n        self.assertEquals(tooltip, '=A2')\n\n    @snapshot_on_error\n    def test_formatted_values_are_shown_in_tooltips(self):\n        # Harold logs on and creates a sheet\n        self.login_and_create_new_sheet()\n\n        # Harold enters a piece of text into a cell. Harold knows the basics of\n        # spreadsheets...\n        self.enter_cell_text(1, 1, 'Maude')\n        self.wait_for_spinner_to_stop()\n\n        # He then sees that a tooltip is set on the cell that would show up if\n        # he hovered over it\n\n        tooltip = self.selenium.get_attribute(\n                self.get_cell_formatted_value_locator(1, 1) + '@title'\n        )\n\n        self.assertEquals(tooltip, 'Maude')\n"
  },
  {
    "path": "dirigible/fts/tests/test_2873_IE_Warning.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom functionaltest import FunctionalTest\n\nclass Test_2873_IE_Warning(FunctionalTest):\n\n    def test_IE_warnings(self):\n        should_warning_be_visible = 'iexplore' in self.selenium.browserStartCommand\n\n        # Harold goes to the Dirigible home page\n        self.go_to_url('/')\n\n        # No warning here, either way...\n        self.wait_for_element_presence('id=id_ie_warning', False)\n\n        # He clicks on the signup link\n        self.click_link(\"id_signup_link\")\n\n        # If he's using IE, he spots a warning sign telling him he ain't\n        # weclome round these parts. If he isn't, he doesn't.\n        self.wait_for_element_presence('id=id_ie_warning', should_warning_be_visible)\n\n        # * He then logs in and goes to a new sheet\n        self.login_and_create_new_sheet()\n\n        # If he's using IE, he spots a warning sign telling him he ain't\n        # weclome round these parts. If he isn't, he doesn't.\n        self.wait_for_element_presence('id=id_ie_warning', should_warning_be_visible)\n\n        # He goes back to his account page and sees a warning there too\n        self.go_to_url('/')\n        self.wait_for_element_presence('id=id_ie_warning', should_warning_be_visible)\n\n\n\n"
  },
  {
    "path": "dirigible/fts/tests/test_2884_FeedbackForm.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd.\n# All Rights Reserved\n#\n\nfrom functionaltest import FunctionalTest, Url\n\n\nclass Test_2884_FeedbackForm(FunctionalTest):\n\n    def tearDown(self):\n        FunctionalTest.tearDown(self)\n        self.clear_email_for_address(\n            'harold.testuser-admin@projectdirigible.com',\n            content_filter=self.get_my_username()\n        )\n\n\n    def test_feedback_dialog_from_non_logged_in_page(self):\n        # * Harold is not logged in, and goes to the root Dirigible page.\n        self.go_to_url(Url.ROOT)\n\n        # * he clicks the feedback link\n        self.selenium.click('link=Feedback')\n        self.wait_for_element_visibility('id=id_feedback_dialog', True)\n\n        #  * titled:\n        self.assertEquals('Help us improve',\n            self.get_text('css=.ui-dialog-title')\n        )\n        #  * with some friendly text:\n        self.assertEquals(\n            \"It's always a pleasure to hear from you!\",\n            self.get_text('css=#id_feedback_dialog_blurb_big')\n        )\n        self.assertEquals(\n            \"Ask us a question, or tell us what you love or hate about Dirigible:\",\n            self.get_text('css=#id_feedback_dialog_blurb_small')\n        )\n        #   * with a big freeform input field\n        #     which has the focus\n        self.wait_for_element_visibility('id=id_feedback_dialog_text', True)\n        self.assertTrue(self.is_element_focused('id=id_feedback_dialog_text'))\n\n        #   * an email field:\n        self.wait_for_element_visibility('id=id_feedback_dialog_email_address', True)\n        #   The email field has a value indicating what it's for:\n        self.assertEquals(\n            \"Email address (optional - only necessary if you would like us to contact you)\",\n            self.selenium.get_value('id=id_feedback_dialog_email_address')\n        )\n        #   * that is grey\n        self.assertEquals(self.get_css_property('#id_feedback_dialog_email_address', 'color'), '#808080')\n        self.assertEquals(self.get_css_property('#id_feedback_dialog_email_address', 'font-style'), 'italic')\n\n        #   * and ok and cancel buttons\n        self.wait_for_element_visibility('id=id_feedback_dialog_ok_button', True)\n        self.wait_for_element_visibility('id=id_feedback_dialog_cancel_button', True)\n\n        # * He enters some text into the message field\n        MESSAGE = 'Dear Sirs, your product is teh awesomez!!! love ' + self.get_my_username()\n        self.selenium.type('id=id_feedback_dialog_text', MESSAGE)\n\n        # * He decides he wants us to be able to thank him for his kind words, so he moves to the\n        #   email field\n        self.selenium.focus('id=id_feedback_dialog_email_address')\n\n        # * The prompt text disappears, and the field switches to non-italic non-grey text\n        self.assertEquals(\n            \"\",\n            self.selenium.get_value('id=id_feedback_dialog_email_address')\n        )\n        self.assertEquals(self.get_css_property('#id_feedback_dialog_email_address', 'color'), '#000000')\n        self.assertEquals(self.get_css_property('#id_feedback_dialog_email_address', 'font-style'), 'normal')\n\n        # * He types his email address\n        SUBMITTED_EMAIL_ADDRESS = 'harold@mailinator.com'\n        self.selenium.type('id=id_feedback_dialog_email_address', SUBMITTED_EMAIL_ADDRESS)\n\n        # * Hits cancel and the form goes away wthout doing anything\n        self.selenium.click('id=id_feedback_dialog_cancel_button')\n        self.wait_for_element_visibility('id=id_feedback_dialog', False)\n\n        # * this time he means it, so he opens the form again\n        self.selenium.click('link=Feedback')\n        self.wait_for_element_visibility('id=id_feedback_dialog', True)\n\n        # * His message and email are still there.\n        self.assertEquals(\n            MESSAGE,\n            self.selenium.get_value('id=id_feedback_dialog_text')\n        )\n        self.assertEquals(\n            SUBMITTED_EMAIL_ADDRESS,\n            self.selenium.get_value('id=id_feedback_dialog_email_address')\n        )\n\n        # He clicks OK.\n        self.selenium.click('id=id_feedback_dialog_ok_button')\n\n        # * The dialog goes away\n        self.wait_for_element_visibility('id=id_feedback_dialog', False)\n\n        # * support@resolversystems.com receives an email with the text he\n        #   entered and his email address\n        fromm, to, subject, body = self.pop_email_for_client(\n            'harold.testuser-admin@projectdirigible.com',\n            content_filter=self.get_my_username()\n        )\n        self.assertEquals(fromm, 'support@projectdirigible.com')\n        self.assertEquals(to, 'harold.testuser-admin@projectdirigible.com')\n        self.assertEquals(subject, '[Django] User feedback from Dirigible')\n        self.assertTrue(MESSAGE in body)\n        self.assertTrue(SUBMITTED_EMAIL_ADDRESS in body)\n        self.assertTrue(\"Page: %s\" % (self.browser.current_url,) in body)\n\n\n    def test_feedback_dialog_from_logged_in_dashboard(self):\n        # * Harold logs in to Dirigible\n        sheet_id = self.login()\n\n        # * he clicks the feedback link\n        self.selenium.click('link=Feedback')\n        self.wait_for_element_visibility('id=id_feedback_dialog', True)\n\n        #  * titled:\n        self.assertEquals('Help us improve',\n            self.get_text('css=.ui-dialog-title')\n        )\n        #  * with some friendly text:\n        self.assertEquals(\n            \"It's always a pleasure to hear from you!\",\n            self.get_text('css=#id_feedback_dialog_blurb_big')\n        )\n        self.assertEquals(\n            \"Ask us a question, or tell us what you love or hate about Dirigible:\",\n            self.get_text('css=#id_feedback_dialog_blurb_small')\n        )\n        #   * with a big freeform input field\n        #     which has the focus\n        self.wait_for_element_visibility('id=id_feedback_dialog_text', True)\n        self.assertTrue(self.is_element_focused('id=id_feedback_dialog_text'))\n\n        #   * There is no email field\n        self.wait_for_element_visibility('id=id_feedback_dialog_email_address', False)\n\n        #   * and ok and cancel buttons\n        self.wait_for_element_visibility('id=id_feedback_dialog_ok_button', True)\n        self.wait_for_element_visibility('id=id_feedback_dialog_cancel_button', True)\n\n        # * He enters some text into the message field\n        MESSAGE = 'Dear Sirs, your product is teh awesomez!!! love ' + self.get_my_username()\n        self.selenium.type('id=id_feedback_dialog_text', MESSAGE)\n\n        # * Hits cancel and the form goes away wthout doing anything\n        self.selenium.click('id=id_feedback_dialog_cancel_button')\n        self.wait_for_element_visibility('id=id_feedback_dialog', False)\n\n        # * this time he means it, so he opens the form again\n        self.selenium.click('link=Feedback')\n        self.wait_for_element_visibility('id=id_feedback_dialog', True)\n\n        # * His message is still there.\n        self.assertEquals(\n            MESSAGE,\n            self.selenium.get_value('id=id_feedback_dialog_text')\n        )\n\n        # * He hits OK\n        self.selenium.click('id=id_feedback_dialog_ok_button')\n\n        # * The dialog goes away\n        self.wait_for_element_visibility('id=id_feedback_dialog', False)\n\n        # * support@resolversystems.com receives an email with the text he\n        #   entered and his username\n        fromm, to, subject, body = self.pop_email_for_client(\n            'harold.testuser-admin@projectdirigible.com',\n            content_filter=self.get_my_username()\n        )\n        self.assertEquals(fromm, 'support@projectdirigible.com')\n        self.assertEquals(to, 'harold.testuser-admin@projectdirigible.com')\n        self.assertEquals(subject, '[Django] User feedback from Dirigible')\n        self.assertTrue(MESSAGE in body)\n        self.assertTrue(\"Username: %s\" % (self.get_my_username(),) in body)\n        self.assertTrue(\"Page: %s\" % (self.browser.current_url,) in body)\n\n\n    def test_feedback_dialog_displays_submission_status(self):\n        # * Harold is not logged in, and goes to the root Dirigible page.\n        self.go_to_url(Url.ROOT)\n\n        self.selenium.get_eval(\"\"\"\n            (function () {\n                var oldAjax = window.$.ajax;\n                function slowAjax(params) {\n                    setTimeout(\n                        function() { oldAjax(params); },\n                        7000\n                    );\n                }\n                window.$.ajax = slowAjax;\n            })()\n        \"\"\");\n\n        # * he clicks the feedback link\n        self.selenium.click('link=Feedback')\n        self.wait_for_element_visibility('id=id_feedback_dialog', True)\n\n        # * He enters some text into the message field\n        MESSAGE = 'Dear Sirs, your product is teh awesomez!!! love ' + self.get_my_username()\n        self.selenium.type('id=id_feedback_dialog_text', MESSAGE)\n\n        # * He types his email address\n        SUBMITTED_EMAIL_ADDRESS = 'harold@mailinator.com'\n        self.selenium.type('id=id_feedback_dialog_email_address', SUBMITTED_EMAIL_ADDRESS)\n\n        # Something goes wrong with the Dirigible server\n        old_feedback_url = self.selenium.get_eval(\"window.urls.feedback\")\n        self.selenium.get_eval(\"window.urls.feedback = 'blergh'\")\n\n        # Blissfully unaware of this, Harold clicks OK.\n        self.selenium.click('id=id_feedback_dialog_ok_button')\n\n        # The dialog remains, the buttons go disabled\n        self.wait_for(\n            lambda: not self.is_element_enabled('id_feedback_dialog_ok_button'),\n            lambda: 'ok button to become disabled'\n        )\n        self.wait_for(\n            lambda: not self.is_element_enabled('id_feedback_dialog_cancel_button'),\n            lambda: 'cancel button to become disabled'\n        )\n\n        # an error div appears to tell him that something is wrong.\n        self.wait_for_element_visibility('id=id_feedback_dialog', True)\n        self.wait_for_element_visibility('id=id_feedback_dialog_error', True)\n\n        # the buttons become enabled again\n        self.wait_for(\n            lambda: self.is_element_enabled('id_feedback_dialog_ok_button'),\n            lambda: 'ok button to become enabled'\n        )\n        self.wait_for(\n            lambda: self.is_element_enabled('id_feedback_dialog_cancel_button'),\n            lambda: 'cancel button to become enabled'\n        )\n\n        # He waits for a moment, and the server magically fixes itself.\n        self.selenium.get_eval(\"window.urls.feedback = '%s'\" % (old_feedback_url,))\n\n        # He tries again.\n        self.selenium.click('id=id_feedback_dialog_ok_button')\n\n        # The error message disappears immediately\n        self.wait_for_element_visibility('id=id_feedback_dialog_error', False, timeout_seconds=3)\n\n        # * The dialog goes away\n        self.wait_for_element_visibility('id=id_feedback_dialog', False)\n\n        # * He brings up the dialog again\n        self.selenium.click('link=Feedback')\n        self.wait_for_element_visibility('id=id_feedback_dialog', True)\n\n        # * He sees that the buttons are enabled and the error message is absent.\n        self.wait_for(\n            lambda: self.is_element_enabled('id_feedback_dialog_ok_button'),\n            lambda: 'ok button to become enabled'\n        )\n        self.wait_for(\n            lambda: self.is_element_enabled('id_feedback_dialog_cancel_button'),\n            lambda: 'cancel button to become enabled'\n        )\n        self.wait_for_element_visibility('id=id_feedback_dialog_error', False)\n"
  },
  {
    "path": "dirigible/fts/tests/test_data/csv_file.csv",
    "content": "1,2,3\na,b,c\n£12.95,Sacrébleu!,\n"
  },
  {
    "path": "dirigible/fts/tests/test_data/excel_generated_csv.csv",
    "content": "some text,\"text with quotes \"\"'\"\"\"\"'\"\"\",\"text with a \ncarriage return\"\r\nsome european characters:,Herg,\r\nsome european money:,pounds: ,euros : \r\nnumbers,,\r\n1,2,3000000000\r\n"
  },
  {
    "path": "dirigible/fts/tests/test_data/expected_csv_file.csv",
    "content": ",,,,\r\n,\"Data at A2, from a constant\",4,,\r\n,\"Data at A3, with a \\n that might need escaping\",,,\r\n,\"Data at A4, with 'single quotes'\",,,hellooooo there!\r\n,\"Data at A5, with \"\"double quotes\"\"\",,,\"[u'list item 1', u'list item 2', 3]\"\r\n,\"Data at A6, with some unicode: Sacr bleu! The  is expensive, compared to the !\",,,\"{u'oats': u'a cereal which in England is fed to horses, but in Scotland forms the sustenance of the nation'}\"\r\n,,,,\r\n"
  },
  {
    "path": "dirigible/fts/tests/test_data/expected_unicode_csv.csv",
    "content": "ゼロウィング\r\n"
  },
  {
    "path": "dirigible/fts/tests/test_data/japanese.csv",
    "content": "新世紀エヴァンゲリオン"
  },
  {
    "path": "dirigible/fts/tests/test_data/public_sheet_csv_file.csv",
    "content": ",\r\n,\r\n,23\r\n,25\r\n"
  },
  {
    "path": "dirigible/info_pages/__init__.py",
    "content": ""
  },
  {
    "path": "dirigible/info_pages/migrations/__init__.py",
    "content": ""
  },
  {
    "path": "dirigible/info_pages/models.py",
    "content": "from django.db import models\n\n# Create your models here.\n"
  },
  {
    "path": "dirigible/info_pages/templates/non_logged_in_front_page.html",
    "content": "{% extends \"base.html\" %}\n\n{% block title %}\n    Welcome to Dirigible, a programmable cloud spreadsheet\n{% endblock %}\n\n\n{% block head %}\n    {{ block.super }}\n    <link rel=\"stylesheet\" href=\"/static/dirigible/styles/non_sheet_page.css\" type=\"text/css\" media=\"screen\" charset=\"utf-8\" />\n    <link rel=\"stylesheet\" href=\"/static/dirigible/styles/registration.css\" type=\"text/css\" media=\"screen\" charset=\"utf-8\" />\n    <link rel=\"stylesheet\" href=\"/static/dirigible/styles/index.css\" type=\"text/css\" media=\"screen\" charset=\"utf-8\" />\n{% endblock %}\n\n\n{% block body %}\n    <div id=\"id_container\">\n        <div id=\"id_content\">\n\n            <div id=\"id_header\" class=\"centered-block\">\n                {% include \"header_links_include.html\" %}\n            </div>\n\n\n            <div id=\"id_big_logo_wrap\" class=\"centered-block\">\n                <a href=\"/\">\n                    <img id=\"id_big_logo\" src=\"/static/dirigible/images/logo_transparent_490x112.png\" alt=\"Dirigible Logo\" title=\"Dirigible Logo\" />\n                </a>\n            </div>\n\n            <div id=\"id_grey_stripe_hwrap\">\n                <div id=\"id_grey_stripe_vwrap\" class=\"centered-block\">\n\n                    <div class=\"id_dirigible_tagline\">\n                        Dirigible is a programmable cloud spreadsheet.\n                    </div>\n\n                    <div id=\"id_more_info_buttons\">\n                        <!-- NB the images/links below must be run together as otherwise we get excess space between them -->\n                           href='{% url 'info_page' \"video\" %}'><img id=\"id_watch_a_video\"\n                                                                    src=\"/static/dirigible/images/watch-a-video.png\"\n                                                                    alt=\"Watch a video\"\n                                                                    title=\"Watch a video\" /></a>\n                    </div>\n\n                    <div class=\"id_dirigible_tagline\" id=\"id_featured_sheets_front_page\">\n                        See some <a href=\"{% url 'featured_sheets' %}\">example spreadsheets</a>.\n                    </div>\n\n                </div>\n            </div>\n\n            <a id=\"id_signup_call_to_action\" href=\"{% url 'registration_register' %}\">\n                <div id=\"id_signup_call_to_action_div\" class=\"centered-block grey_rounded_corner_box\">\n\n                    Sign up now!\n\n                </div>\n            </a>\n\n            {% include \"footer_links_include.html\" %}\n\n        </div>\n\n    </div>\n\n{% endblock %}\n"
  },
  {
    "path": "dirigible/info_pages/templates/oss.html",
    "content": "{% extends \"info_page.html\" %}\n\n{% block title %}\n    Thanks to Open Source Projects: Dirigible\n{% endblock %}\n\n{% block head %}\n    {{ block.super }}\n    <link rel=\"stylesheet\" href=\"/static/dirigible/styles/info_page.css\" type=\"text/css\" media=\"screen\" charset=\"utf-8\" />\n{% endblock %}\n\n\n{% block middle %}\n\n    <h1>Thanks!</h1>\n\n    <p>\n    It would have been much harder to create Dirigible without a bunch of great\n    Open Source projects, and we'd like to take this opportunity to say \"thanks!\"\n\n    <p>\n    The most visible projects we use are:\n\n    <ul>\n        <li>Michael Leibman's excellent <a href=\"https://github.com/mleibman/SlickGrid\">SlickGrid</a>,\n            an essential part of a programmable cloud <i>spreadsheet</i>.\n        <li>Almost as important for the <i>programmable</i> part is the\n            <a href=\"http://ace.ajax.org/\">Ajax.org Cloud9 Editor</a>, aka ACE.\n        <li>Dirigible is, of course, programmable in <a href=\"http://www.python.org/\">Python</a>.\n    </ul>\n\n    <p>\n    There are tons of great Open Source libraries for Python, and we've made sure that some of the best\n    are pre-installed:\n\n    <ul>\n        <li><a href=\"http://numpy.scipy.org/\">NumPy</a>, the fundamental package needed for scientific computing with Python.\n        <li><a href=\"http://gmpy.sourceforge.net/\">GmPy</a>, provides multiprecision arithmetic\n        <li><a href=\"http://code.google.com/p/mpmath/\">MpMath</a>, another library for multiprecision floating-point arithmetic.\n        <li>The <a href=\"http://www.scipy.org/\">SciPy</a> library, providing scientific tools for mathematics, science, and engineering.\n        <li><a href=\"http://www.dlitz.net/software/pycrypto/\">PyCrypto</a>, the Python cryptography toolkit.\n        <li><a href=\"http://www.sqlalchemy.org/\">SqlAlchemy</a>, a Python SQL toolkit and object relational mapper.\n        <li><a href=\"http://codespeak.net/lxml/\">LXML</a>, for working with XML and HTML.\n        <li><a href=\"http://pypi.python.org/pypi/xlrd\">xlrd</a>, for reading Excel files.\n        <li><a href=\"http://www.rdflib.net/\">RDFLib</a>, for working with <a href=\"http://www.w3.org/RDF/\">RDF</a>.\n        <li><a href=\"http://code.google.com/p/geopy/\">GeoPy</a>, a geocoding toolbox.\n        <li><a href=\"http://www.crummy.com/software/BeautifulSoup/\">Beautiful Soup</a>, a forgiving HTML parser for real-world web pages.\n        <li><a href=\"http://wwwsearch.sourceforge.net/mechanize/\">Mechanize</a>, stateful programmatic web browsing.\n    </ul>\n\n    <p>\n    Less directly visible to users, our server-side stack includes many\n    open source components, including:\n    <ul>\n        <li>\n        <a href=\"http://www.debian.org/\">Debian</a> the GNU/Linux operating\n                    system on which it all runs.\n        </li>\n\n        <li>\n        <a href=\"http://httpd.apache.org/\">Apache</a>, the world's most\n                    popular web-server.\n        </li>\n\n        <li>\n        <a href=\"http://code.google.com/p/modwsgi/\">Modwsgi</a> for\n                    Apache, connecting our web server to Python.\n        </li>\n\n        <li>\n        <a href=\"http://pypi.python.org/pypi/Django/1.2.5\">Django</a> a\n                    Python web framework.\n        </li>\n\n        <li>\n        <a href=\"http://pypi.python.org/pypi/django-registration/\">\n                    Django-registration</a>, making it easy for users to sign up\n                    to Dirigible.\n        </li>\n\n        <li>\n        <a href=\"http://pypi.python.org/pypi/setuptools\">setuptools</a>,\n                    The Python library to install Python libraries.\n        </li>\n    </ul>\n\n    <p>\n    Finally, back at the coal-face, our programmers use many development tools\n    and libraries, the most prominent of which are:\n\n    <ul>\n        <li>\n        <a href=\"http://pypi.python.org/pypi/unittest2\">Unittest2</a> and\n        <a href=\"http://pypi.python.org/pypi/mock\">Mock</a>,\n                indispensible Python testing libraries by eminent\n                Resolver Systems alumnus Michael Foord.\n        </li>\n\n        <li>\n        <a href=\"http://pypi.python.org/pypi/ply/3.1\">Ply</a>, by David\n                Beazley, allows us to parse our customised Python grammer for cell\n                formulae.\n        </li>\n\n        <li>\n        <a href=\"http://pypi.python.org/pypi/chardet/\">Chardet</a>, by Mark\n                Pilgrim, is a godsend for discerning unicode encodings.\n        </li>\n    </ul>\n\n{% endblock %}\n\n"
  },
  {
    "path": "dirigible/info_pages/templates/video.html",
    "content": "{% extends \"non_sheet_page_small_logo.html\" %}\n\n\n{% block title %}\n    The Video: Dirigible\n{% endblock %}\n\n\n{% block head %}\n    {{ block.super }}\n    <link rel=\"stylesheet\" href=\"/static/dirigible/styles/video.css\" type=\"text/css\" media=\"screen\" charset=\"utf-8\" />\n{% endblock %}\n\n{% block content %}\n    <div class=\"clear\"></div>\n\n    <div class=\"centered-block\">\n        <div id=\"id_video_div\">\n            <iframe title=\"YouTube video player\" class=\"youtube-player\" type=\"text/html\" width=\"960\" height=\"750\" src=\"http://www.youtube.com/embed/2ZoIp-5NaiQ?hd=1\" frameborder=\"0\"></iframe>\n        </div>\n    </div>\n\n{% endblock %}\n"
  },
  {
    "path": "dirigible/info_pages/tests/__init__.py",
    "content": "\n"
  },
  {
    "path": "dirigible/info_pages/tests/test_views.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\r\n# See LICENSE.md\r\n#\r\n\r\nfrom mock import patch, sentinel, Mock\r\n\r\nfrom dirigible.test_utils import ResolverTestCase\r\n\r\nfrom info_pages.views import front_page_view, info_page_view, non_logged_in_front_page_view\r\n\r\n\r\nclass TestFrontPageView(ResolverTestCase):\r\n\r\n    @patch('info_pages.views.non_logged_in_front_page_view')\r\n    def test_should_delegate_to_non_logged_in_front_page_view_if_not_logged_in(self, mock_non_logged_in_front_page_view):\r\n        request = Mock()\r\n        request.user.is_authenticated.return_value = False\r\n        response = front_page_view(request)\r\n        self.assertCalledOnce(mock_non_logged_in_front_page_view, request)\r\n        self.assertEquals(response, mock_non_logged_in_front_page_view.return_value)\r\n\r\n\r\n    @patch('info_pages.views.user_dashboard')\r\n    def test_should_delegate_to_user_dashboard_if_are_logged_in(self, mock_user_dashboard):\r\n        request = Mock()\r\n        request.user.is_authenticated.return_value = True\r\n        response = front_page_view(request)\r\n        self.assertCalledOnce(mock_user_dashboard, request)\r\n        self.assertEquals(response, mock_user_dashboard.return_value)\r\n\r\n\r\n\r\nclass TestNonLoggedInFrontPageView(ResolverTestCase):\r\n\r\n    @patch('info_pages.views.render_to_response')\r\n    def test_should_render_template_to_response_with_registration_form(self, mock_render_to_response):\r\n        response = non_logged_in_front_page_view(sentinel.request)\r\n        self.assertCalledOnce(\r\n            mock_render_to_response,\r\n            'non_logged_in_front_page.html',\r\n            {}\r\n        )\r\n        self.assertEquals(response, mock_render_to_response.return_value)\r\n\r\n\r\n\r\nclass TestInfoPageView(ResolverTestCase):\r\n\r\n    @patch('info_pages.views.render_to_response')\r\n    def test_should_render_template_to_response_with_user(self, mock_render_to_response):\r\n        request = Mock()\r\n        request.user.is_authenticated.return_value = False\r\n        response = info_page_view(request, \"my_template_name\")\r\n        self.assertCalledOnce(\r\n            mock_render_to_response,\r\n            'my_template_name.html',\r\n            {\r\n                'user': request.user\r\n            }\r\n        )\r\n        self.assertEquals(response, mock_render_to_response.return_value)\r\n"
  },
  {
    "path": "dirigible/info_pages/views.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\nfrom django.shortcuts import render_to_response\n\nfrom user.views import user_dashboard\n\n\ndef front_page_view(request):\n    if request.user.is_authenticated():\n        return user_dashboard(request)\n    else:\n        return non_logged_in_front_page_view(request)\n\n\ndef non_logged_in_front_page_view(request):\n    return render_to_response('non_logged_in_front_page.html', {})\n\n\ndef info_page_view(request, template_name):\n    return render_to_response(\n        '%s.html' % (template_name,), {'user': request.user}\n    )\n"
  },
  {
    "path": "dirigible/manage.py",
    "content": "#!/usr/bin/env python\nimport os\nimport sys\n\nif __name__ == \"__main__\":\n    os.environ.setdefault(\"DJANGO_SETTINGS_MODULE\", \"dirigible.settings\")\n\n    from django.core.management import execute_from_command_line\n\n    execute_from_command_line(sys.argv)\n"
  },
  {
    "path": "dirigible/registration/__init__.py",
    "content": ""
  },
  {
    "path": "dirigible/registration/admin.py",
    "content": "from django.contrib import admin\n\nfrom registration.models import RegistrationProfile\n\n\nclass RegistrationAdmin(admin.ModelAdmin):\n    list_display = ('__unicode__', 'activation_key_expired')\n    search_fields = ('user__username', 'user__first_name')\n\n\nadmin.site.register(RegistrationProfile, RegistrationAdmin)\n"
  },
  {
    "path": "dirigible/registration/forms.py",
    "content": "\"\"\"\nForms and validation code for user registration.\n\n\"\"\"\n\n\nfrom django import forms\nfrom django.utils.translation import ugettext_lazy as _\nfrom django.contrib.auth.models import User\n\nfrom registration.models import RegistrationProfile\n\n\n# I put this on all required fields, because it's easier to pick up\n# on them with CSS or JavaScript if they have a class of \"required\"\n# in the HTML. Your mileage may vary. If/when Django ticket #3515\n# lands in trunk, this will no longer be necessary.\nattrs_dict = { 'class': 'required' }\n\n\nclass RegistrationForm(forms.Form):\n    \"\"\"\n    Form for registering a new user account.\n    \n    Validates that the requested username is not already in use, and\n    requires the password to be entered twice to catch typos.\n    \n    Subclasses should feel free to add any additional validation they\n    need, but should either preserve the base ``save()`` or implement\n    a ``save()`` which accepts the ``profile_callback`` keyword\n    argument and passes it through to\n    ``RegistrationProfile.objects.create_inactive_user()``.\n    \n    \"\"\"\n    username = forms.RegexField(regex=r'^\\w+$',\n                                max_length=30,\n                                widget=forms.TextInput(attrs=attrs_dict),\n                                label=_(u'username'))\n    email = forms.EmailField(widget=forms.TextInput(attrs=dict(attrs_dict,\n                                                               maxlength=75)),\n                             label=_(u'email address'))\n    password1 = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict, render_value=False),\n                                label=_(u'password'))\n    password2 = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict, render_value=False),\n                                label=_(u'password (again)'))\n    \n    def clean_username(self):\n        \"\"\"\n        Validate that the username is alphanumeric and is not already\n        in use.\n        \n        \"\"\"\n        try:\n            user = User.objects.get(username__iexact=self.cleaned_data['username'])\n        except User.DoesNotExist:\n            return self.cleaned_data['username']\n        raise forms.ValidationError(_(u'This username is already taken. Please choose another.'))\n\n    def clean(self):\n        \"\"\"\n        Verifiy that the values entered into the two password fields\n        match. Note that an error here will end up in\n        ``non_field_errors()`` because it doesn't apply to a single\n        field.\n        \n        \"\"\"\n        if 'password1' in self.cleaned_data and 'password2' in self.cleaned_data:\n            if self.cleaned_data['password1'] != self.cleaned_data['password2']:\n                raise forms.ValidationError(_(u'You must type the same password each time'))\n        return self.cleaned_data\n    \n    def save(self, profile_callback=None):\n        \"\"\"\n        Create the new ``User`` and ``RegistrationProfile``, and\n        returns the ``User``.\n        \n        This is essentially a light wrapper around\n        ``RegistrationProfile.objects.create_inactive_user()``,\n        feeding it the form data and a profile callback (see the\n        documentation on ``create_inactive_user()`` for details) if\n        supplied.\n        \n        \"\"\"\n        new_user = RegistrationProfile.objects.create_inactive_user(username=self.cleaned_data['username'],\n                                                                    password=self.cleaned_data['password1'],\n                                                                    email=self.cleaned_data['email'],\n                                                                    profile_callback=profile_callback)\n        return new_user\n\n\nclass RegistrationFormTermsOfService(RegistrationForm):\n    \"\"\"\n    Subclass of ``RegistrationForm`` which adds a required checkbox\n    for agreeing to a site's Terms of Service.\n    \n    \"\"\"\n    tos = forms.BooleanField(widget=forms.CheckboxInput(attrs=attrs_dict),\n                             label=_(u'I have read and agree to the Terms of Service'),\n                             error_messages={ 'required': u\"You must agree to the terms to register\" })\n\n\nclass RegistrationFormUniqueEmail(RegistrationForm):\n    \"\"\"\n    Subclass of ``RegistrationForm`` which enforces uniqueness of\n    email addresses.\n    \n    \"\"\"\n    def clean_email(self):\n        \"\"\"\n        Validate that the supplied email address is unique for the\n        site.\n        \n        \"\"\"\n        if User.objects.filter(email__iexact=self.cleaned_data['email']):\n            raise forms.ValidationError(_(u'This email address is already in use. Please supply a different email address.'))\n        return self.cleaned_data['email']\n\n\nclass RegistrationFormNoFreeEmail(RegistrationForm):\n    \"\"\"\n    Subclass of ``RegistrationForm`` which disallows registration with\n    email addresses from popular free webmail services; moderately\n    useful for preventing automated spam registrations.\n    \n    To change the list of banned domains, subclass this form and\n    override the attribute ``bad_domains``.\n    \n    \"\"\"\n    bad_domains = ['aim.com', 'aol.com', 'email.com', 'gmail.com',\n                   'googlemail.com', 'hotmail.com', 'hushmail.com',\n                   'msn.com', 'mail.ru', 'mailinator.com', 'live.com']\n    \n    def clean_email(self):\n        \"\"\"\n        Check the supplied email address against a list of known free\n        webmail domains.\n        \n        \"\"\"\n        email_domain = self.cleaned_data['email'].split('@')[1]\n        if email_domain in self.bad_domains:\n            raise forms.ValidationError(_(u'Registration using free email addresses is prohibited. Please supply a different email address.'))\n        return self.cleaned_data['email']\n"
  },
  {
    "path": "dirigible/registration/locale/ar/LC_MESSAGES/django.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same license as the PACKAGE package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: PACKAGE VERSION\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2007-09-19 19:30-0500\\n\"\n\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\n#: forms.py:38\nmsgid \"username\"\nmsgstr \"اسم المستخدم\"\n\n#: forms.py:41\nmsgid \"email address\"\nmsgstr \"عنوان البريد الالكتروني\"\n\n#: forms.py:43\nmsgid \"password\"\nmsgstr \"كلمة المرور\"\n\n#: forms.py:45\nmsgid \"password (again)\"\nmsgstr \"تأكيد كلمة المرور\"\n\n#: forms.py:54\nmsgid \"Usernames can only contain letters, numbers and underscores\"\nmsgstr \"يمكن أن يحتوي اسم المستخدم على احرف، ارقام وشرطات سطرية فقط\"\n\n#: forms.py:59\nmsgid \"This username is already taken. Please choose another.\"\nmsgstr \"اسم المستخدم مسجل مسبقا. يرجى اختيار اسم اخر.\"\n\n#: forms.py:68\nmsgid \"You must type the same password each time\"\nmsgstr \"يجب ادخال كلمة المرور مطابقة كل مرة\"\n\n#: forms.py:96\nmsgid \"I have read and agree to the Terms of Service\"\nmsgstr \"أقر بقراءة والموافقة على شروط الخدمة\"\n\n#: forms.py:105\nmsgid \"You must agree to the terms to register\"\nmsgstr \"يجب الموافقة على الشروط للتسجيل\"\n\n#: forms.py:124\nmsgid \"\"\n\"This email address is already in use. Please supply a different email \"\n\"address.\"\nmsgstr \"عنوان البريد الالكتروني مسجل مسبقا. يرجى تزويد عنوان بريد الكتروني مختلف.\"\n\n#: forms.py:149\nmsgid \"\"\n\"Registration using free email addresses is prohibited. Please supply a \"\n\"different email address.\"\nmsgstr \"يمنع التسجيل باستخدام عناوين بريد الكترونية مجانية. يرجى تزويد عنوان بريد الكتروني مختلف.\"\n\n#: models.py:188\nmsgid \"user\"\nmsgstr \"مستخدم\"\n\n#: models.py:189\nmsgid \"activation key\"\nmsgstr \"رمز التفعيل\"\n\n#: models.py:194\nmsgid \"registration profile\"\nmsgstr \"ملف التسجيل الشخصي\"\n\n#: models.py:195\nmsgid \"registration profiles\"\nmsgstr \"ملفات التسجيل الشخصية\"\n"
  },
  {
    "path": "dirigible/registration/locale/bg/LC_MESSAGES/django.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same license as the PACKAGE package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.\n#\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: PACKAGE VERSION\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2007-09-19 19:30-0500\\n\"\n\"PO-Revision-Date: 2008-03-05 12:37+0200\\n\"\n\"Last-Translator: Vladislav <vladislav.mitov@gmail.com>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"X-Poedit-Bookmarks: -1,-1,-1,-1,10,-1,-1,-1,-1,-1\\n\"\n\n#: forms.py:38\nmsgid \"username\"\nmsgstr \"Потребителско име \"\n\n#: forms.py:41\nmsgid \"email address\"\nmsgstr \"Електронна поща\"\n\n#: forms.py:43\nmsgid \"password\"\nmsgstr \"Парола\"\n\n#: forms.py:45\nmsgid \"password (again)\"\nmsgstr \"Парола (проверка)\"\n\n#: forms.py:54\nmsgid \"Usernames can only contain letters, numbers and underscores\"\nmsgstr \"Потребителските имена могат да съдържат букви, цифри и подчертавки\"\n\n#: forms.py:59\nmsgid \"This username is already taken. Please choose another.\"\nmsgstr \"Потребителското име е заето. Моля изберето друго.\"\n\n#: forms.py:68\nmsgid \"You must type the same password each time\"\nmsgstr \"Грешка при проверка на паролата.\"\n\n#: forms.py:96\nmsgid \"I have read and agree to the Terms of Service\"\nmsgstr \"Прочел съм и съм съгласен с условията за експлоатация\"\n\n#: forms.py:105\nmsgid \"You must agree to the terms to register\"\nmsgstr \"Трябва да сте съгласни с условията за да се регистрирате.\"\n\n#: forms.py:124\nmsgid \"This email address is already in use. Please supply a different email address.\"\nmsgstr \"Адреса на електронната поща е използван. Моля въведете друг адрес.\"\n\n#: forms.py:149\nmsgid \"Registration using free email addresses is prohibited. Please supply a different email address.\"\nmsgstr \"Регистрациите с безплатни адреси е забранен. Моля въведете различен адрес за електронна поща\"\n\n#: models.py:188\nmsgid \"user\"\nmsgstr \"Потребител\"\n\n#: models.py:189\nmsgid \"activation key\"\nmsgstr \"Ключ за активация\"\n\n#: models.py:194\nmsgid \"registration profile\"\nmsgstr \"регистрационен профил\"\n\n#: models.py:195\nmsgid \"registration profiles\"\nmsgstr \"регистрационни профили\"\n\n"
  },
  {
    "path": "dirigible/registration/locale/de/LC_MESSAGES/django.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same license as the PACKAGE package.\n# Jannis Leidel <jannis@leidel.info>, 2007.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: django-registration 0.3 \\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2007-09-19 19:30-0500\\n\"\n\"PO-Revision-Date: 2007-09-29 16:50+0200\\n\"\n\"Last-Translator: Jannis Leidel <jannis@leidel.info>\\n\"\n\"Language-Team: Deutsch <de@li.org>\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\n#: forms.py:38\nmsgid \"username\"\nmsgstr \"Benutzername\"\n\n#: forms.py:41\nmsgid \"email address\"\nmsgstr \"E-Mail-Adresse\"\n\n#: forms.py:43\nmsgid \"password\"\nmsgstr \"Passwort\"\n\n#: forms.py:45\nmsgid \"password (again)\"\nmsgstr \"Passwort (wiederholen)\"\n\n#: forms.py:54\nmsgid \"Usernames can only contain letters, numbers and underscores\"\nmsgstr \"Benutzernamen können nur Buchstaben, Zahlen und Unterstriche enthalten\"\n\n#: forms.py:59\nmsgid \"This username is already taken. Please choose another.\"\nmsgstr \"Dieser Benutzername ist schon vergeben. Bitte einen anderen wählen.\"\n\n#: forms.py:68\nmsgid \"You must type the same password each time\"\nmsgstr \"Bitte das gleiche Passwort zur Überprüfung nochmal eingeben\"\n\n#: forms.py:96\nmsgid \"I have read and agree to the Terms of Service\"\nmsgstr \"Ich habe die Nutzungsvereinbarung gelesen und stimme ihr zu\"\n\n#: forms.py:105\nmsgid \"You must agree to the terms to register\"\nmsgstr \"Sie müssen der Nutzungsvereinbarung zustimmen, um sich zu registrieren\"\n\n#: forms.py:124\nmsgid \"\"\n\"This email address is already in use. Please supply a different email \"\n\"address.\"\nmsgstr \"\"\n\"Diese E-Mail-Adresse wird schon genutzt. Bitte geben Sie eine andere \"\n\"E-Mail-Adresse an.\"\n\n#: forms.py:149\nmsgid \"\"\n\"Registration using free email addresses is prohibited. Please supply a \"\n\"different email address.\"\nmsgstr \"\"\n\"Die Registrierung mit einer kostenlosen E-Mail-Adresse ist untersagt. Bitte \"\n\"geben Sie eine andere E-Mail-Adresse an.\"\n\n#: models.py:188\nmsgid \"user\"\nmsgstr \"Benutzer\"\n\n#: models.py:189\nmsgid \"activation key\"\nmsgstr \"Aktivierungsschlüssel\"\n\n#: models.py:194\nmsgid \"registration profile\"\nmsgstr \"Registrierungsprofil\"\n\n#: models.py:195\nmsgid \"registration profiles\"\nmsgstr \"Registrierungsprofile\"\n"
  },
  {
    "path": "dirigible/registration/locale/el/LC_MESSAGES/django.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same license as the PACKAGE package.\n# Panos Laganakos <panos.laganakos@gmail.com>, 2007.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: PACKAGE VERSION\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2007-09-19 19:30-0500\\n\"\n\"PO-Revision-Date: 2007-11-14 21:50+0200\\n\"\n\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\n#: forms.py:38\nmsgid \"username\"\nmsgstr \"όνομα χρήστη\"\n\n#: forms.py:41\nmsgid \"email address\"\nmsgstr \"διεύθυνση ηλεκτρονικού ταχυδρομείου\"\n\n#: forms.py:43\nmsgid \"password\"\nmsgstr \"συνθηματικό\"\n\n#: forms.py:45\nmsgid \"password (again)\"\nmsgstr \"συνθηματικό (ξανά)\"\n\n#: forms.py:54\nmsgid \"Usernames can only contain letters, numbers and underscores\"\nmsgstr \"Τα ονόματα χρηστών μπορούν να περιλαμβάνουν μόνο γράμματα, αριθμούς και υπογραμμίσεις\"\n\n#: forms.py:59\nmsgid \"This username is already taken. Please choose another.\"\nmsgstr \"Αυτό το όνομα χρήστη χρησιμοποίειται ήδη. Παρακαλώ διαλέξτε ένα άλλο.\"\n\n#: forms.py:68\nmsgid \"You must type the same password each time\"\nmsgstr \"Πρέπει να εισάγετε το ίδιο συνθηματικό κάθε φορά\"\n\n#: forms.py:96\nmsgid \"I have read and agree to the Terms of Service\"\nmsgstr \"Διάβασα και συμφωνώ με τους Όρους της Υπηρεσίας\"\n\n#: forms.py:105\nmsgid \"You must agree to the terms to register\"\nmsgstr \"Πρέπει να συμφωνείται με τους όρους για να εγγραφείτε\"\n\n#: forms.py:124\nmsgid \"\"\n\"This email address is already in use. Please supply a different email \"\n\"address.\"\nmsgstr \"\"\n\"Η συγκεκριμένη διεύθυνση ηλεκτρονικού ταχυδρομείου χρησιμοποιείται ήδη. \"\n\"Παρακαλώ δώστε κάποια άλλη.\"\n\n#: forms.py:149\nmsgid \"\"\n\"Registration using free email addresses is prohibited. Please supply a \"\n\"different email address.\"\nmsgstr \"\"\n\"Η εγγραφή μέσω δωρεάν διευθύνσεων ηλεκτρονικού ταχυδρομείου απαγορεύεται. \"\"Παρακαλώ δώστε κάποια άλλη.\"\n\n#: models.py:188\nmsgid \"user\"\nmsgstr \"χρήστης\"\n\n#: models.py:189\nmsgid \"activation key\"\nmsgstr \"κλειδί ενεργοποίησης\"\n\n#: models.py:194\nmsgid \"registration profile\"\nmsgstr \"προφίλ εγγραφής\"\n\n#: models.py:195\nmsgid \"registration profiles\"\nmsgstr \"προφίλ εγγραφών\"\n"
  },
  {
    "path": "dirigible/registration/locale/en/LC_MESSAGES/django.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same license as the PACKAGE package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: PACKAGE VERSION\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2007-09-19 19:30-0500\\n\"\n\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\n#: forms.py:38\nmsgid \"username\"\nmsgstr \"\"\n\n#: forms.py:41\nmsgid \"email address\"\nmsgstr \"\"\n\n#: forms.py:43\nmsgid \"password\"\nmsgstr \"\"\n\n#: forms.py:45\nmsgid \"password (again)\"\nmsgstr \"\"\n\n#: forms.py:54\nmsgid \"Usernames can only contain letters, numbers and underscores\"\nmsgstr \"\"\n\n#: forms.py:59\nmsgid \"This username is already taken. Please choose another.\"\nmsgstr \"\"\n\n#: forms.py:68\nmsgid \"You must type the same password each time\"\nmsgstr \"\"\n\n#: forms.py:96\nmsgid \"I have read and agree to the Terms of Service\"\nmsgstr \"\"\n\n#: forms.py:105\nmsgid \"You must agree to the terms to register\"\nmsgstr \"\"\n\n#: forms.py:124\nmsgid \"\"\n\"This email address is already in use. Please supply a different email \"\n\"address.\"\nmsgstr \"\"\n\n#: forms.py:149\nmsgid \"\"\n\"Registration using free email addresses is prohibited. Please supply a \"\n\"different email address.\"\nmsgstr \"\"\n\n#: models.py:188\nmsgid \"user\"\nmsgstr \"\"\n\n#: models.py:189\nmsgid \"activation key\"\nmsgstr \"\"\n\n#: models.py:194\nmsgid \"registration profile\"\nmsgstr \"\"\n\n#: models.py:195\nmsgid \"registration profiles\"\nmsgstr \"\"\n"
  },
  {
    "path": "dirigible/registration/locale/es/LC_MESSAGES/django.po",
    "content": "# Spanish translation for django-registration.\n# Copyright (C) 2007, James Bennet\n# This file is distributed under the same license as the registration package.\n# Ernesto Rico Schmidt <e.rico.schmidt@gmail.com>, 2008.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: django-registration 0.3 \\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2008-03-11 00:19-0400\\n\"\n\"PO-Revision-Date: 2008-03-11 00:19-0400\\n\"\n\"Last-Translator: Ernesto Rico Schmidt <e.rico.schmidt@gmail.com>\\n\"\n\"Language-Team: Español <de@li.org>\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\n#: forms.py:38\nmsgid \"username\"\nmsgstr \"nombre de usuario\"\n\n#: forms.py:41\nmsgid \"email address\"\nmsgstr \"dirección de coreo electrónico\"\n\n#: forms.py:43\nmsgid \"password\"\nmsgstr \"contraseña\"\n\n#: forms.py:45\nmsgid \"password (again)\"\nmsgstr \"contraseña (otra vez)\"\n\n#: forms.py:54\nmsgid \"Usernames can only contain letters, numbers and underscores\"\nmsgstr \"Los nombres de usuarios sólo pueden contener letras, números y guiones bajos\"\n\n#: forms.py:59\nmsgid \"This username is already taken. Please choose another.\"\nmsgstr \"Este nombre de usuario ya está ocupado. Por favor escoge otro\"\n\n#: forms.py:71\nmsgid \"You must type the same password each time\"\nmsgstr \"Tienes que introducir la misma contraseña cada vez\"\n\n#: forms.py:100\nmsgid \"I have read and agree to the Terms of Service\"\nmsgstr \"He leído y acepto los términos de servicio\"\n\n#: forms.py:109\nmsgid \"You must agree to the terms to register\"\nmsgstr \"Tienes que aceptar los términos para registrarte\"\n\n#: forms.py:128\nmsgid \"\"\n\"This email address is already in use. Please supply a different email \"\n\"address.\"\nmsgstr \"\"\n\"La dirección de correo electrónico ya está siendo usada. Por favor\"\n\"proporciona otra dirección.\"\n\n#: forms.py:153\nmsgid \"\"\n\"Registration using free email addresses is prohibited. Please supply a \"\n\"different email address.\"\nmsgstr \"\"\n\"El registro usando una dirección de correo electrónico gratis está prohibido.\"\n\"Por favor proporciona otra dirección.\"\n\n#: models.py:188\nmsgid \"user\"\nmsgstr \"usuario\"\n\n#: models.py:189\nmsgid \"activation key\"\nmsgstr \"clave de activación\"\n\n#: models.py:194\nmsgid \"registration profile\"\nmsgstr \"perfil de registro\"\n\n#: models.py:195\nmsgid \"registration profiles\"\nmsgstr \"perfiles de registro\"\n"
  },
  {
    "path": "dirigible/registration/locale/es_AR/LC_MESSAGES/django.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) 2008 Leonardo Manuel Rocha\n# This file is distributed under the same license as the PACKAGE package.\n# FIRST AUTHOR <l e o m a r o at g m a i l dot c o m>, YEAR.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: PACKAGE VERSION\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2007-09-19 19:30-0500\\n\"\n\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\n#: forms.py:38\nmsgid \"username\"\nmsgstr \"nombre de usuario\"\n\n#: forms.py:41\nmsgid \"email address\"\nmsgstr \"dirección de e-mail\"\n\n#: forms.py:43\nmsgid \"password\"\nmsgstr \"contraseña\"\n\n#: forms.py:45\nmsgid \"password (again)\"\nmsgstr \"contraseña (nuevamente)\"\n\n#: forms.py:54\nmsgid \"Usernames can only contain letters, numbers and underscores\"\nmsgstr \"El nombre de usuario solo puede contener letras, números y guiones bajos\"\n\n#: forms.py:59\nmsgid \"This username is already taken. Please choose another.\"\nmsgstr \"Ese nombre de usuario ya está asignado. Por favor elija otro.\"\n\n#: forms.py:68\nmsgid \"You must type the same password each time\"\nmsgstr \"Debe tipear la misma contraseña cada vez\"\n\n#: forms.py:96\nmsgid \"I have read and agree to the Terms of Service\"\nmsgstr \"He leído y estoy de acuerdo con las Condiciones de Servicio\"\n\n#: forms.py:105\nmsgid \"You must agree to the terms to register\"\nmsgstr \"Debe estar de acuerdo con las Condiciones para poder registrarse\"\n\n#: forms.py:124\nmsgid \"\"\n\"This email address is already in use. Please supply a different email \"\n\"address.\"\nmsgstr \"Esa dirección de e-mail ya está en uso. Por favor provea otra \"\n\"dirección.\"\n\n#: forms.py:149\nmsgid \"\"\n\"Registration using free email addresses is prohibited. Please supply a \"\n\"different email address.\"\nmsgstr \"La registración con un e-mail gratuito está prohibida. Por favor \"\n\"de una dirección de e-mail diferente.\"\n\n#: models.py:188\nmsgid \"user\"\nmsgstr \"usuario\"\n\n#: models.py:189\nmsgid \"activation key\"\nmsgstr \"clave de activación\"\n\n#: models.py:194\nmsgid \"registration profile\"\nmsgstr \"perfil de registro\"\n\n#: models.py:195\nmsgid \"registration profiles\"\nmsgstr \"perfiles de registro\"\n"
  },
  {
    "path": "dirigible/registration/locale/fr/LC_MESSAGES/django.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same license as the PACKAGE package.\n# Samuel Adam <samuel.adam@gmail.com>, 2007.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: django-registration 0.3 \\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2007-09-19 19:30-0500\\n\"\n\"PO-Revision-Date: 2007-09-20 10:30+0100\\n\"\n\"Last-Translator: Samuel Adam <samuel.adam@gmail.com>\\n\"\n\"Language-Team: Français <fr@li.org>\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\n#: forms.py:38\nmsgid \"username\"\nmsgstr \"pseudo\"\n\n#: forms.py:41\nmsgid \"email address\"\nmsgstr \"adresse email\"\n\n#: forms.py:43\nmsgid \"password\"\nmsgstr \"mot de passe\"\n\n#: forms.py:45\nmsgid \"password (again)\"\nmsgstr \"mot de passe (vérification)\"\n\n#: forms.py:54\nmsgid \"Usernames can only contain letters, numbers and underscores\"\nmsgstr \"Le pseudo ne peut contenir que des lettres, chiffres et le caractère souligné.\"\n\n#: forms.py:59\nmsgid \"This username is already taken. Please choose another.\"\nmsgstr \"Ce pseudo est déjà utilisé. Veuillez en choisir un autre.\"\n\n#: forms.py:68\nmsgid \"You must type the same password each time\"\nmsgstr \"Veuillez indiquer le même mot de passe dans les deux champs\"\n\n#: forms.py:96\nmsgid \"I have read and agree to the Terms of Service\"\nmsgstr \"J'ai lu et accepté les Conditions Générales d'Utilisation\"\n\n#: forms.py:105\nmsgid \"You must agree to the terms to register\"\nmsgstr \"Vous devez accepter les conditions d'utilisation pour vous inscrire\"\n\n#: forms.py:124\nmsgid \"\"\n\"This email address is already in use. Please supply a different email \"\n\"address.\"\nmsgstr \"Cette adresse email est déjà utilisée. Veuillez en indiquer une autre.\"\n\n#: forms.py:149\nmsgid \"\"\n\"Registration using free email addresses is prohibited. Please supply a \"\n\"different email address.\"\nmsgstr \"L'inscription avec une adresse email d'un compte gratuit est interdite. Veuillez en indiquer une autre.\"\n\n#: models.py:188\nmsgid \"user\"\nmsgstr \"utilisateur\"\n\n#: models.py:189\nmsgid \"activation key\"\nmsgstr \"clé d'activation\"\n\n#: models.py:194\nmsgid \"registration profile\"\nmsgstr \"profil d'inscription\"\n\n#: models.py:195\nmsgid \"registration profiles\"\nmsgstr \"profils d'inscription\"\n"
  },
  {
    "path": "dirigible/registration/locale/he/LC_MESSAGES/django.po",
    "content": "# translation of registration.\n# Copyright (C) 2008 THE registration'S COPYRIGHT HOLDER\n# This file is distributed under the same license as the registration package.\n# <>, 2008.\n# , fuzzy\n#  <>, 2008.\n# \n# \nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: registration\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2008-02-10 02:01+0200\\n\"\n\"PO-Revision-Date: 2008-02-10 02:05+0200\\n\"\n\"Last-Translator: Meir Kriheli <meir@mksoft.co.il>\\n\"\n\"Language-Team: Hebrew\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\"\n\n#: forms.py:38\nmsgid \"username\"\nmsgstr \"שם משתמש\"\n\n#: forms.py:41\nmsgid \"email address\"\nmsgstr \"דואר אלקטרוני\"\n\n#: forms.py:43\nmsgid \"password\"\nmsgstr \"סיסמה\"\n\n#: forms.py:45\nmsgid \"password (again)\"\nmsgstr \"סיסמה (שוב)\"\n\n#: forms.py:54\nmsgid \"Usernames can only contain letters, numbers and underscores\"\nmsgstr \"שמות משתמש יכולים להכיל רק אותיות, ספרות וקווים תחתונים\"\n\n#: forms.py:59\nmsgid \"This username is already taken. Please choose another.\"\nmsgstr \"שם המשתמש תפוס כבר. נא לבחור אחר.\"\n\n#: forms.py:64\nmsgid \"You must type the same password each time\"\nmsgstr \"יש להקליד את אותה הסיסמה פעמיים\"\n\n#: forms.py:93\nmsgid \"I have read and agree to the Terms of Service\"\nmsgstr \"קראתי והסכמתי לתנאי השימוש\"\n\n#: forms.py:102\nmsgid \"You must agree to the terms to register\"\nmsgstr \"עליך להסכים לתנאי השימוש\"\n\n#: forms.py:121\nmsgid \"\"\n\"This email address is already in use. Please supply a different email \"\n\"address.\"\nmsgstr \"\"\n\"כתובת הדואר האלקטרוני תפוסה כבר. נא לספק כתובת דואר אחרת.\"\n\n#: forms.py:146\nmsgid \"\"\n\"Registration using free email addresses is prohibited. Please supply a \"\n\"different email address.\"\nmsgstr \"\"\n\"הרישום בעזרת תיבת דואר אלקטרוני חינמית אסור. נא לספק כתובת אחרת.\"\n\n#: models.py:188\nmsgid \"user\"\nmsgstr \"משתמש\"\n\n#: models.py:189\nmsgid \"activation key\"\nmsgstr \"מפתח הפעלה\"\n\n#: models.py:194\nmsgid \"registration profile\"\nmsgstr \"פרופיל רישום\"\n\n#: models.py:195\nmsgid \"registration profiles\"\nmsgstr \"פרופילי רישום\"\n\n"
  },
  {
    "path": "dirigible/registration/locale/it/LC_MESSAGES/django.po",
    "content": "# translation of django.po to Italiano\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same license as the PACKAGE package.\n#\n# Nicola Larosa <nico@tekNico.net>, 2008.\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: django\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2007-09-19 19:30-0500\\n\"\n\"PO-Revision-Date: 2008-05-27 15:05+0200\\n\"\n\"Last-Translator: Nicola Larosa <nico@tekNico.net>\\n\"\n\"Language-Team: Italiano\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"X-Generator: KBabel 1.11.4\\n\"\n\"Plural-Forms:  nplurals=2; plural=(n != 1);\\n\"\n\n#: forms.py:38\nmsgid \"username\"\nmsgstr \"nome utente\"\n\n#: forms.py:41\nmsgid \"email address\"\nmsgstr \"indirizzo email\"\n\n#: forms.py:43\nmsgid \"password\"\nmsgstr \"password\"\n\n#: forms.py:45\nmsgid \"password (again)\"\nmsgstr \"password (di nuovo)\"\n\n#: forms.py:54\nmsgid \"Usernames can only contain letters, numbers and underscores\"\nmsgstr \"I nomi utente possono contenere solo lettere, numeri e sottolineature\"\n\n#: forms.py:59\nmsgid \"This username is already taken. Please choose another.\"\nmsgstr \"Questo nome utente è già usato. Scegline un altro.\"\n\n#: forms.py:68\nmsgid \"You must type the same password each time\"\nmsgstr \"Bisogna inserire la stessa password ogni volta\"\n\n#: forms.py:96\nmsgid \"I have read and agree to the Terms of Service\"\nmsgstr \"Dichiaro di aver letto e di approvare le Condizioni di Servizio\"\n\n#: forms.py:105\nmsgid \"You must agree to the terms to register\"\nmsgstr \"Per registrarsi bisogna approvare le condizioni\"\n\n#: forms.py:124\nmsgid \"This email address is already in use. Please supply a different email \"\n\"address.\"\nmsgstr \"Questo indirizzo email è già in uso. Inserisci un altro indirizzo email.\"\n\n#: forms.py:149\nmsgid \"Registration using free email addresses is prohibited. Please supply a \"\n\"different email address.\"\nmsgstr \"La registrazione con indirizzi email gratis non è permessa. \"\n\"Inserisci un altro indirizzo email.\"\n\n#: models.py:188\nmsgid \"user\"\nmsgstr \"utente\"\n\n#: models.py:189\nmsgid \"activation key\"\nmsgstr \"chiave di attivazione\"\n\n#: models.py:194\nmsgid \"registration profile\"\nmsgstr \"profilo di registrazione\"\n\n#: models.py:195\nmsgid \"registration profiles\"\nmsgstr \"profili di registrazione\"\n\n"
  },
  {
    "path": "dirigible/registration/locale/ja/LC_MESSAGES/django.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same license as the PACKAGE package.\n# Shinya Okano <xxshss@yahoo.co.jp>, YEAR.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: django-registration 0.4 \\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2007-09-19 19:30-0500\\n\"\n\"PO-Revision-Date: 2008-01-31 10:20+0900\\n\"\n\"Last-Translator: Shinya Okano <xxshss@yahoo.co.jp>\\n\"\n\"Language-Team: Japanese <LL@li.org>\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\n#: forms.py:38\nmsgid \"username\"\nmsgstr \"ユーザ名\"\n\n#: forms.py:41\nmsgid \"email address\"\nmsgstr \"メールアドレス\"\n\n#: forms.py:43\nmsgid \"password\"\nmsgstr \"パスワード\"\n\n#: forms.py:45\nmsgid \"password (again)\"\nmsgstr \"パスワード (確認)\"\n\n#: forms.py:54\nmsgid \"Usernames can only contain letters, numbers and underscores\"\nmsgstr \"ユーザ名には半角英数とアンダースコアのみが使用できます。\"\n\n#: forms.py:59\nmsgid \"This username is already taken. Please choose another.\"\nmsgstr \"このユーザ名は既に使用されています。他のユーザ名を指定してください。\"\n\n#: forms.py:68\nmsgid \"You must type the same password each time\"\nmsgstr \"同じパスワードを入力する必要があります。\"\n\n#: forms.py:96\nmsgid \"I have read and agree to the Terms of Service\"\nmsgstr \"サービス利用規約を読み、同意します。\"\n\n#: forms.py:105\nmsgid \"You must agree to the terms to register\"\nmsgstr \"登録するためには規約に同意する必要があります。\"\n\n#: forms.py:124\nmsgid \"This email address is already in use. Please supply a different email address.\"\nmsgstr \"このメールアドレスは既に使用されています。他のメールアドレスを指定して下さい。\"\n\n#: forms.py:149\nmsgid \"Registration using free email addresses is prohibited. Please supply a different email address.\"\nmsgstr \"自由なメールアドレスを使用した登録は禁止されています。他のメールアドレスを指定してください。\"\n\n#: models.py:188\nmsgid \"user\"\nmsgstr \"ユーザ\"\n\n#: models.py:189\nmsgid \"activation key\"\nmsgstr \"アクティベーションキー\"\n\n#: models.py:194\nmsgid \"registration profile\"\nmsgstr \"登録プロファイル\"\n\n#: models.py:195\nmsgid \"registration profiles\"\nmsgstr \"登録プロファイル\"\n\n"
  },
  {
    "path": "dirigible/registration/locale/nl/LC_MESSAGES/django.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same license as the PACKAGE package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.\n# \nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: registration\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2008-08-14 13:25+0200\\n\"\n\"PO-Revision-Date: 2008-08-14 13:25+0200\\n\"\n\"Last-Translator: Joost Cassee <joost@cassee.net>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=2; plural=(n != 1);\\n\"\n\n#: forms.py:38\nmsgid \"username\"\nmsgstr \"gebruikersnaam\"\n\n#: forms.py:41\nmsgid \"email address\"\nmsgstr \"e-mail adres\"\n\n#: forms.py:43\nmsgid \"password\"\nmsgstr \"wachtwoord\"\n\n#: forms.py:45\nmsgid \"password (again)\"\nmsgstr \"wachtwoord (opnieuw)\"\n\n#: forms.py:54\nmsgid \"Usernames can only contain letters, numbers and underscores\"\nmsgstr \"Gebruikersnamen kunnen alleen letters, nummer en liggende streepjes bevatten.\"\n\n#: forms.py:59\nmsgid \"This username is already taken. Please choose another.\"\nmsgstr \"Deze gebruikersnaam is reeds in gebruik. Kiest u alstublieft een andere gebruikersnaam.\"\n\n#: forms.py:71\nmsgid \"You must type the same password each time\"\nmsgstr \"U moet twee maal hetzelfde wachtwoord typen.\"\n\n#: forms.py:100\nmsgid \"I have read and agree to the Terms of Service\"\nmsgstr \"Ik heb de servicevoorwaarden gelezen en ga akkoord.\"\n\n#: forms.py:109\nmsgid \"You must agree to the terms to register\"\nmsgstr \"U moet akkoord gaan met de servicevoorwaarden om u te registreren.\"\n\n#: forms.py:125\nmsgid \"This email address is already in use. Please supply a different email address.\"\nmsgstr \"Dit e-mail adres is reeds in gebruik. Kiest u alstublieft een ander e-mail adres.\"\n\n#: forms.py:151\nmsgid \"Registration using free email addresses is prohibited. Please supply a different email address.\"\nmsgstr \"U kunt u niet registreren met een gratis e-mail adres. Kiest u alstublieft een ander e-mail adres.\"\n\n#: models.py:191\nmsgid \"user\"\nmsgstr \"gebruiker\"\n\n#: models.py:192\nmsgid \"activation key\"\nmsgstr \"activatiecode\"\n\n#: models.py:197\nmsgid \"registration profile\"\nmsgstr \"registratieprofiel\"\n\n#: models.py:198\nmsgid \"registration profiles\"\nmsgstr \"registratieprofielen\"\n"
  },
  {
    "path": "dirigible/registration/locale/pl/LC_MESSAGES/django.po",
    "content": "# Polish translation for django-registration.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same license as the django-registration package.\n# Jarek Zgoda <jarek.zgoda@gmail.com>, 2007.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: 0.4\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2007-09-19 19:30-0500\\n\"\n\"PO-Revision-Date: 2007-12-15 12:45+0100\\n\"\n\"Last-Translator: Jarek Zgoda <jarek.zgoda@gmail.com>\\n\"\n\"Language-Team: Polish <LL@li.org>\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\n#: forms.py:38\nmsgid \"username\"\nmsgstr \"nazwa użytkownika\"\n\n#: forms.py:41\nmsgid \"email address\"\nmsgstr \"adres email\"\n\n#: forms.py:43\nmsgid \"password\"\nmsgstr \"hasło\"\n\n#: forms.py:45\nmsgid \"password (again)\"\nmsgstr \"hasło (ponownie)\"\n\n#: forms.py:54\nmsgid \"Usernames can only contain letters, numbers and underscores\"\nmsgstr \"\"\n\"Nazwa użytkownika może zawierać tylko litery, cyfry i znaki podkreślenia\"\n\n#: forms.py:59\nmsgid \"This username is already taken. Please choose another.\"\nmsgstr \"Ta nazwa użytkownika jest już zajęta. Wybierz inną.\"\n\n#: forms.py:68\nmsgid \"You must type the same password each time\"\nmsgstr \"Musisz wpisać to samo hasło w obu polach\"\n\n#: forms.py:96\nmsgid \"I have read and agree to the Terms of Service\"\nmsgstr \"Przeczytałem regulamin i akceptuję go\"\n\n#: forms.py:105\nmsgid \"You must agree to the terms to register\"\nmsgstr \"Musisz zaakceptować regulamin, aby się zarejestrować\"\n\n#: forms.py:124\nmsgid \"\"\n\"This email address is already in use. Please supply a different email \"\n\"address.\"\nmsgstr \"Ten adres email jest już używany. Użyj innego adresu email.\"\n\n#: forms.py:149\nmsgid \"\"\n\"Registration using free email addresses is prohibited. Please supply a \"\n\"different email address.\"\nmsgstr \"\"\n\"Nie ma możliwości rejestracji przy użyciu darmowego adresu email. Użyj \"\n\"innego adresu email.\"\n\n#: models.py:188\nmsgid \"user\"\nmsgstr \"użytkownik\"\n\n#: models.py:189\nmsgid \"activation key\"\nmsgstr \"klucz aktywacyjny\"\n\n#: models.py:194\nmsgid \"registration profile\"\nmsgstr \"profil rejestracji\"\n\n#: models.py:195\nmsgid \"registration profiles\"\nmsgstr \"profile rejestracji\"\n"
  },
  {
    "path": "dirigible/registration/locale/pt_BR/LC_MESSAGES/django.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same license as the PACKAGE package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: PACKAGE VERSION\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2007-09-19 19:30-0500\\n\"\n\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\n#: forms.py:38\nmsgid \"username\"\nmsgstr \"usuário\"\n\n#: forms.py:41\nmsgid \"email address\"\nmsgstr \"endereço de email\"\n\n#: forms.py:43\nmsgid \"password\"\nmsgstr \"\"\n\n#: forms.py:45\nmsgid \"password (again)\"\nmsgstr \"senha (novamente)\"\n\n#: forms.py:54\nmsgid \"Usernames can only contain letters, numbers and underscores\"\nmsgstr \"Nomes de usuário apenas podem conter letras, números, e underscore\"\n\n#: forms.py:59\nmsgid \"This username is already taken. Please choose another.\"\nmsgstr \"Este nome de usuário já existe. Por favor, escolha outro.\"\n\n#: forms.py:68\nmsgid \"You must type the same password each time\"\nmsgstr \"Você deve escrever a mesma senha nos dois campos\"\n\n#: forms.py:96\nmsgid \"I have read and agree to the Terms of Service\"\nmsgstr \"Eu lí e concordo com os Termos de Uso do serviço\"\n\n#: forms.py:105\nmsgid \"You must agree to the terms to register\"\nmsgstr \"Você deve concordar com os termos para registrar-se\"\n\n#: forms.py:124\nmsgid \"\"\n\"This email address is already in use. Please supply a different email \"\n\"address.\"\nmsgstr \"Este endereço de email já está em uso. Por favor, informe um endereço de email diferente.\"\n\n#: forms.py:149\nmsgid \"\"\n\"Registration using free email addresses is prohibited. Please supply a \"\n\"different email address.\"\nmsgstr \"Registrar-se com contas de email gratuitos está proibido. Por favor, informe um endereço de email diferente.\"\n\n#: models.py:188\nmsgid \"user\"\nmsgstr \"usuário\"\n\n#: models.py:189\nmsgid \"activation key\"\nmsgstr \"chave de ativação\"\n\n#: models.py:194\nmsgid \"registration profile\"\nmsgstr \"profile de registro\"\n\n#: models.py:195\nmsgid \"registration profiles\"\nmsgstr \"profiles de registro\"\n"
  },
  {
    "path": "dirigible/registration/locale/ru/LC_MESSAGES/django.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same license as the PACKAGE package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: PACKAGE VERSION\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2007-09-19 19:30-0500\\n\"\n\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\n#: forms.py:38\nmsgid \"username\"\nmsgstr \"имя пользователя\"\n\n#: forms.py:41\nmsgid \"email address\"\nmsgstr \"адрес электронной почты\"\n\n#: forms.py:43\nmsgid \"password\"\nmsgstr \"пароль\"\n\n#: forms.py:45\nmsgid \"password (again)\"\nmsgstr \"пароль (верификация)\"\n\n#: forms.py:54\nmsgid \"Usernames can only contain letters, numbers and underscores\"\nmsgstr \"Имя пользователя может содержать только буквы, цифры и подчеркивания\"\n\n#: forms.py:59\nmsgid \"This username is already taken. Please choose another.\"\nmsgstr \"Такое имя пользователя уже есть. Пожалуйста, выберите другое.\"\n\n#: forms.py:68\nmsgid \"You must type the same password each time\"\nmsgstr \"Вы должны вводить один и тот же пароль каждый раз\"\n\n#: forms.py:96\nmsgid \"I have read and agree to the Terms of Service\"\nmsgstr \"Я прочитал и согласен с Правилами Использования\"\n\n#: forms.py:105\nmsgid \"You must agree to the terms to register\"\nmsgstr \"Вы должны согласиться с Правилами для регистрации\"\n\n#: forms.py:124\nmsgid \"\"\n\"This email address is already in use. Please supply a different email \"\n\"address.\"\nmsgstr \"Этот адрес электронной почты уже используется. Пожалуйста, введите другой адрес.\"\n\n#: forms.py:149\nmsgid \"\"\n\"Registration using free email addresses is prohibited. Please supply a \"\n\"different email address.\"\nmsgstr \"Регистрация с использованием свободных почтовых серверов запрещена. Пожалуйста, введите другой адрес электронной почты.\"\n\n#: models.py:188\nmsgid \"user\"\nmsgstr \"пользователь\"\n\n#: models.py:189\nmsgid \"activation key\"\nmsgstr \"ключ активации\"\n\n#: models.py:194\nmsgid \"registration profile\"\nmsgstr \"профиль регистрации\"\n\n#: models.py:195\nmsgid \"registration profiles\"\nmsgstr \"профили регистрации\"\n"
  },
  {
    "path": "dirigible/registration/locale/sr/LC_MESSAGES/django.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same license as the PACKAGE package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.\n#\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: django-registration trunk\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2008-04-05 13:51+0200\\n\"\n\"PO-Revision-Date: 2008-04-05 14:00+0100\\n\"\n\"Last-Translator: Nebojsa Djordjevic <djnesh@gmail.com>\\n\"\n\"Language-Team: \\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\\n\"\n\"X-Poedit-Language: Serbian\\n\"\n\"X-Poedit-Country: YUGOSLAVIA\\n\"\n\n#: forms.py:38\nmsgid \"username\"\nmsgstr \"korisničko ime\"\n\n#: forms.py:41\nmsgid \"email address\"\nmsgstr \"email adresa\"\n\n#: forms.py:43\nmsgid \"password\"\nmsgstr \"šifra\"\n\n#: forms.py:45\nmsgid \"password (again)\"\nmsgstr \"šifra (ponovo)\"\n\n#: forms.py:54\nmsgid \"Usernames can only contain letters, numbers and underscores\"\nmsgstr \"Korisničko ime može da se sastoji samo od slova, brojeva i donje crte (\\\"_\\\")\"\n\n#: forms.py:59\nmsgid \"This username is already taken. Please choose another.\"\nmsgstr \"Korisničko ime je već zauzeto. Izaberite drugo.\"\n\n#: forms.py:71\nmsgid \"You must type the same password each time\"\nmsgstr \"Unete šifre se ne slažu\"\n\n#: forms.py:100\nmsgid \"I have read and agree to the Terms of Service\"\nmsgstr \"Pročitao sam i slažem se sa uslovima korišćenja\"\n\n#: forms.py:109\nmsgid \"You must agree to the terms to register\"\nmsgstr \"Morate se složiti sa uslovima korišćenja da bi ste se registrovali\"\n\n#: forms.py:128\nmsgid \"This email address is already in use. Please supply a different email address.\"\nmsgstr \"Ova e-mail adresa je već u upotrebi. Morate koristiti drugu e-mail adresu.\"\n\n#: forms.py:153\nmsgid \"Registration using free email addresses is prohibited. Please supply a different email address.\"\nmsgstr \"Registracija korišćenjem besplatnig e-mail adresa je zabranjena. Morate uneti drugu e-mail adresu.\"\n\n#: models.py:188\nmsgid \"user\"\nmsgstr \"korisnik\"\n\n#: models.py:189\nmsgid \"activation key\"\nmsgstr \"aktivacioni ključ\"\n\n#: models.py:194\nmsgid \"registration profile\"\nmsgstr \"registracioni profil\"\n\n#: models.py:195\nmsgid \"registration profiles\"\nmsgstr \"registracioni profili\"\n\n"
  },
  {
    "path": "dirigible/registration/locale/sv/LC_MESSAGES/django.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same license as the PACKAGE package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: PACKAGE VERSION\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2008-03-23 18:59+0100\\n\"\n\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n\"Last-Translator: Emil Stenström <em@kth.se>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\n#: .\\forms.py:38 \nmsgid \"username\"\nmsgstr \"Användarnamn\"\n\n#: .\\forms.py:41 \nmsgid \"email address\"\nmsgstr \"E-postadress\"\n\n#: .\\forms.py:43 \nmsgid \"password\"\nmsgstr \"Lösenord\"\n\n#: .\\forms.py:45 \nmsgid \"password (again)\"\nmsgstr \"Lösenord (igen)\"\n\n#: .\\forms.py:54 \nmsgid \"Usernames can only contain letters, numbers and underscores\"\nmsgstr \"Användarnamn får bara innehålla bokstäver, siffror och understreck\"\n\n#: .\\forms.py:59 \nmsgid \"This username is already taken. Please choose another.\"\nmsgstr \"Det användarnamnet är upptaget. Prova ett annat.\"\n\n#: .\\forms.py:71 \nmsgid \"You must type the same password each time\"\nmsgstr \"Båda lösenord måste vara lika\"\n\n#: .\\forms.py:100 \nmsgid \"I have read and agree to the Terms of Service\"\nmsgstr \"Jag har läst och accepterar avtalet\"\n\n#: .\\forms.py:109 \nmsgid \"You must agree to the terms to register\"\nmsgstr \"Du måste acceptera avtalet för att registrera dig\"\n\n#: .\\forms.py:128 \nmsgid \"\"\n\"This email address is already in use. Please supply a different email \"\n\"address.\"\nmsgstr \"Den e-postadressen är upptagen, använd an annan adress.\"\n\n#: .\\forms.py:153 \nmsgid \"\"\n\"Registration using free email addresses is prohibited. Please supply a \"\n\"different email address.\"\nmsgstr \"Gratis e-postadresser är inte tillåtna, använd en annan adress.\"\n\n#: .\\models.py:188 \nmsgid \"user\"\nmsgstr \"Användare\"\n\n#: .\\models.py:189 \nmsgid \"activation key\"\nmsgstr \"Aktiveringsnyckel\"\n\n#: .\\models.py:194 \nmsgid \"registration profile\"\nmsgstr \"Profil\"\n\n#: .\\models.py:195 \nmsgid \"registration profiles\"\nmsgstr \"Profiler\"\n"
  },
  {
    "path": "dirigible/registration/locale/zh_CN/LC_MESSAGES/django.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same license as the PACKAGE package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.\n#\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: PACKAGE VERSION\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2007-09-19 19:30-0500\\n\"\n\"PO-Revision-Date: 2008-03-20 23:22+0800\\n\"\n\"Last-Translator: hutuworm <hutuworm@gmail.com>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\n#: forms.py:38\nmsgid \"username\"\nmsgstr \"用户名\"\n\n#: forms.py:41\nmsgid \"email address\"\nmsgstr \"Email 地址\"\n\n#: forms.py:43\nmsgid \"password\"\nmsgstr \"密码\"\n\n#: forms.py:45\nmsgid \"password (again)\"\nmsgstr \"密码（重复）\"\n\n#: forms.py:54\nmsgid \"Usernames can only contain letters, numbers and underscores\"\nmsgstr \"用户名只能包含字母、数字和下划线\"\n\n#: forms.py:59\nmsgid \"This username is already taken. Please choose another.\"\nmsgstr \"该用户名已被占用，请另选一个。\"\n\n#: forms.py:68\nmsgid \"You must type the same password each time\"\nmsgstr \"您必须输入两遍同样的密码\"\n\n#: forms.py:96\nmsgid \"I have read and agree to the Terms of Service\"\nmsgstr \"我已阅读并同意该服务条款\"\n\n#: forms.py:105\nmsgid \"You must agree to the terms to register\"\nmsgstr \"您必须同意注册条款\"\n\n#: forms.py:124\nmsgid \"This email address is already in use. Please supply a different email address.\"\nmsgstr \"该 Email 地址已有人使用，请提供一个另外的 Email 地址。\"\n\n#: forms.py:149\nmsgid \"Registration using free email addresses is prohibited. Please supply a different email address.\"\nmsgstr \"禁止使用免费 Email 地址注册，请提供一个另外的 Email 地址。\"\n\n#: models.py:188\nmsgid \"user\"\nmsgstr \"用户\"\n\n#: models.py:189\nmsgid \"activation key\"\nmsgstr \"激活密钥\"\n\n#: models.py:194\nmsgid \"registration profile\"\nmsgstr \"注册信息\"\n\n#: models.py:195\nmsgid \"registration profiles\"\nmsgstr \"注册信息\"\n\n"
  },
  {
    "path": "dirigible/registration/locale/zh_TW/LC_MESSAGES/django.po",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same license as the PACKAGE package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.\n#\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: PACKAGE VERSION\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2007-09-19 19:30-0500\\n\"\n\"PO-Revision-Date: 2008-03-20 23:22+0800\\n\"\n\"Last-Translator: hutuworm <hutuworm@gmail.com>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\n#: forms.py:38\nmsgid \"username\"\nmsgstr \"用戶名\"\n\n#: forms.py:41\nmsgid \"email address\"\nmsgstr \"Email 地址\"\n\n#: forms.py:43\nmsgid \"password\"\nmsgstr \"密碼\"\n\n#: forms.py:45\nmsgid \"password (again)\"\nmsgstr \"密碼（重復）\"\n\n#: forms.py:54\nmsgid \"Usernames can only contain letters, numbers and underscores\"\nmsgstr \"用戶名只能包含字母、數字和下劃線\"\n\n#: forms.py:59\nmsgid \"This username is already taken. Please choose another.\"\nmsgstr \"該用戶名已被佔用，請另選一個。\"\n\n#: forms.py:68\nmsgid \"You must type the same password each time\"\nmsgstr \"您必須輸入兩遍同樣的密碼\"\n\n#: forms.py:96\nmsgid \"I have read and agree to the Terms of Service\"\nmsgstr \"我已閱讀並同意該服務條款\"\n\n#: forms.py:105\nmsgid \"You must agree to the terms to register\"\nmsgstr \"您必須同意注冊條款\"\n\n#: forms.py:124\nmsgid \"This email address is already in use. Please supply a different email address.\"\nmsgstr \"該 Email 地址已有人使用，請提供一個另外的 Email 地址。\"\n\n#: forms.py:149\nmsgid \"Registration using free email addresses is prohibited. Please supply a different email address.\"\nmsgstr \"禁止使用免費 Email 地址注冊，請提供一個另外的 Email 地址。\"\n\n#: models.py:188\nmsgid \"user\"\nmsgstr \"用戶\"\n\n#: models.py:189\nmsgid \"activation key\"\nmsgstr \"激活密鑰\"\n\n#: models.py:194\nmsgid \"registration profile\"\nmsgstr \"注冊信息\"\n\n#: models.py:195\nmsgid \"registration profiles\"\nmsgstr \"注冊信息\"\n\n"
  },
  {
    "path": "dirigible/registration/management/__init__.py",
    "content": ""
  },
  {
    "path": "dirigible/registration/management/commands/__init__.py",
    "content": ""
  },
  {
    "path": "dirigible/registration/management/commands/cleanupregistration.py",
    "content": "\"\"\"\nA management command which deletes expired accounts (e.g.,\naccounts which signed up but never activated) from the database.\n\nCalls ``RegistrationProfile.objects.delete_expired_users()``, which\ncontains the actual logic for determining which accounts are deleted.\n\n\"\"\"\n\nfrom django.core.management.base import NoArgsCommand\n\nfrom registration.models import RegistrationProfile\n\n\nclass Command(NoArgsCommand):\n    help = \"Delete expired user registrations from the database\"\n\n    def handle_noargs(self, **options):\n        RegistrationProfile.objects.delete_expired_users()\n"
  },
  {
    "path": "dirigible/registration/migrations/0001_initial.py",
    "content": "# -*- coding: utf-8 -*-\nfrom __future__ import unicode_literals\n\nfrom django.db import models, migrations\nfrom django.conf import settings\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        migrations.swappable_dependency(settings.AUTH_USER_MODEL),\n    ]\n\n    operations = [\n        migrations.CreateModel(\n            name='RegistrationProfile',\n            fields=[\n                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),\n                ('activation_key', models.CharField(max_length=40, verbose_name='activation key')),\n                ('user', models.ForeignKey(verbose_name='user', to=settings.AUTH_USER_MODEL, unique=True)),\n            ],\n            options={\n                'verbose_name': 'registration profile',\n                'verbose_name_plural': 'registration profiles',\n            },\n            bases=(models.Model,),\n        ),\n    ]\n"
  },
  {
    "path": "dirigible/registration/migrations/__init__.py",
    "content": ""
  },
  {
    "path": "dirigible/registration/models.py",
    "content": "import datetime\nimport random\nimport re\nimport sha\n\nfrom django.conf import settings\nfrom django.db import models\nfrom django.template.loader import render_to_string\nfrom django.utils.translation import ugettext_lazy as _\nfrom django.contrib.auth.models import User\nfrom django.contrib.sites.models import Site\n\n\nSHA1_RE = re.compile('^[a-f0-9]{40}$')\n\n\nclass RegistrationManager(models.Manager):\n    \"\"\"\n    Custom manager for the ``RegistrationProfile`` model.\n    \n    The methods defined here provide shortcuts for account creation\n    and activation (including generation and emailing of activation\n    keys), and for cleaning out expired inactive accounts.\n    \n    \"\"\"\n    def activate_user(self, activation_key):\n        \"\"\"\n        Validate an activation key and activate the corresponding\n        ``User`` if valid.\n        \n        If the key is valid and has not expired, return the ``User``\n        after activating.\n        \n        If the key is not valid or has expired, return ``False``.\n        \n        If the key is valid but the ``User`` is already active,\n        return ``False``.\n        \n        To prevent reactivation of an account which has been\n        deactivated by site administrators, the activation key is\n        reset to the string ``ALREADY_ACTIVATED`` after successful\n        activation.\n        \n        \"\"\"\n        # Make sure the key we're trying conforms to the pattern of a\n        # SHA1 hash; if it doesn't, no point trying to look it up in\n        # the database.\n        if SHA1_RE.search(activation_key):\n            try:\n                profile = self.get(activation_key=activation_key)\n            except self.model.DoesNotExist:\n                return False\n            if not profile.activation_key_expired():\n                user = profile.user\n                user.is_active = True\n                user.save()\n                profile.activation_key = self.model.ACTIVATED\n                profile.save()\n                return user\n        return False\n    \n    def create_inactive_user(self, username, password, email,\n                             send_email=True, profile_callback=None):\n        \"\"\"\n        Create a new, inactive ``User``, generates a\n        ``RegistrationProfile`` and email its activation key to the\n        ``User``, returning the new ``User``.\n        \n        To disable the email, call with ``send_email=False``.\n\n        The activation email will make use of two templates:\n\n        ``registration/activation_email_subject.txt``\n            This template will be used for the subject line of the\n            email. It receives one context variable, ``site``, which\n            is the currently-active\n            ``django.contrib.sites.models.Site`` instance. Because it\n            is used as the subject line of an email, this template's\n            output **must** be only a single line of text; output\n            longer than one line will be forcibly joined into only a\n            single line.\n\n        ``registration/activation_email.txt``\n            This template will be used for the body of the email. It\n            will receive three context variables: ``activation_key``\n            will be the user's activation key (for use in constructing\n            a URL to activate the account), ``expiration_days`` will\n            be the number of days for which the key will be valid and\n            ``site`` will be the currently-active\n            ``django.contrib.sites.models.Site`` instance.\n        \n        To enable creation of a custom user profile along with the\n        ``User`` (e.g., the model specified in the\n        ``AUTH_PROFILE_MODULE`` setting), define a function which\n        knows how to create and save an instance of that model with\n        appropriate default values, and pass it as the keyword\n        argument ``profile_callback``. This function should accept one\n        keyword argument:\n\n        ``user``\n            The ``User`` to relate the profile to.\n        \n        \"\"\"\n        new_user = User.objects.create_user(username, email, password)\n        new_user.is_active = False\n        new_user.save()\n        \n        registration_profile = self.create_profile(new_user)\n        \n        if profile_callback is not None:\n            profile_callback(user=new_user)\n        \n        if send_email:\n            from django.core.mail import send_mail\n            current_site = Site.objects.get_current()\n            \n            subject = render_to_string('registration/activation_email_subject.txt',\n                                       { 'site': current_site })\n            # Email subject *must not* contain newlines\n            subject = ''.join(subject.splitlines())\n            \n            message = render_to_string('registration/activation_email.txt',\n                                       { 'activation_key': registration_profile.activation_key,\n                                         'expiration_days': settings.ACCOUNT_ACTIVATION_DAYS,\n                                         'site': current_site })\n            \n            send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [new_user.email])\n        return new_user\n    \n    def create_profile(self, user):\n        \"\"\"\n        Create a ``RegistrationProfile`` for a given\n        ``User``, and return the ``RegistrationProfile``.\n        \n        The activation key for the ``RegistrationProfile`` will be a\n        SHA1 hash, generated from a combination of the ``User``'s\n        username and a random salt.\n        \n        \"\"\"\n        salt = sha.new(str(random.random())).hexdigest()[:5]\n        activation_key = sha.new(salt+user.username).hexdigest()\n        return self.create(user=user,\n                           activation_key=activation_key)\n        \n    def delete_expired_users(self):\n        \"\"\"\n        Remove expired instances of ``RegistrationProfile`` and their\n        associated ``User``s.\n        \n        Accounts to be deleted are identified by searching for\n        instances of ``RegistrationProfile`` with expired activation\n        keys, and then checking to see if their associated ``User``\n        instances have the field ``is_active`` set to ``False``; any\n        ``User`` who is both inactive and has an expired activation\n        key will be deleted.\n        \n        It is recommended that this method be executed regularly as\n        part of your routine site maintenance; this application\n        provides a custom management command which will call this\n        method, accessible as ``manage.py cleanupregistration``.\n        \n        Regularly clearing out accounts which have never been\n        activated serves two useful purposes:\n        \n        1. It alleviates the ocasional need to reset a\n           ``RegistrationProfile`` and/or re-send an activation email\n           when a user does not receive or does not act upon the\n           initial activation email; since the account will be\n           deleted, the user will be able to simply re-register and\n           receive a new activation key.\n        \n        2. It prevents the possibility of a malicious user registering\n           one or more accounts and never activating them (thus\n           denying the use of those usernames to anyone else); since\n           those accounts will be deleted, the usernames will become\n           available for use again.\n        \n        If you have a troublesome ``User`` and wish to disable their\n        account while keeping it in the database, simply delete the\n        associated ``RegistrationProfile``; an inactive ``User`` which\n        does not have an associated ``RegistrationProfile`` will not\n        be deleted.\n        \n        \"\"\"\n        for profile in self.all():\n            if profile.activation_key_expired():\n                user = profile.user\n                if not user.is_active:\n                    user.delete()\n\n\nclass RegistrationProfile(models.Model):\n    \"\"\"\n    A simple profile which stores an activation key for use during\n    user account registration.\n    \n    Generally, you will not want to interact directly with instances\n    of this model; the provided manager includes methods\n    for creating and activating new accounts, as well as for cleaning\n    out accounts which have never been activated.\n    \n    While it is possible to use this model as the value of the\n    ``AUTH_PROFILE_MODULE`` setting, it's not recommended that you do\n    so. This model's sole purpose is to store data temporarily during\n    account registration and activation, and a mechanism for\n    automatically creating an instance of a site-specific profile\n    model is provided via the ``create_inactive_user`` on\n    ``RegistrationManager``.\n    \n    \"\"\"\n    ACTIVATED = u\"ALREADY_ACTIVATED\"\n    \n    user = models.ForeignKey(User, unique=True, verbose_name=_('user'))\n    activation_key = models.CharField(_('activation key'), max_length=40)\n    \n    objects = RegistrationManager()\n    \n    class Meta:\n        verbose_name = _('registration profile')\n        verbose_name_plural = _('registration profiles')\n    \n    def __unicode__(self):\n        return u\"Registration information for %s\" % self.user\n    \n    def activation_key_expired(self):\n        \"\"\"\n        Determine whether this ``RegistrationProfile``'s activation\n        key has expired, returning a boolean -- ``True`` if the key\n        has expired.\n        \n        Key expiration is determined by a two-step process:\n        \n        1. If the user has already activated, the key will have been\n           reset to the string ``ALREADY_ACTIVATED``. Re-activating is\n           not permitted, and so this method returns ``True`` in this\n           case.\n\n        2. Otherwise, the date the user signed up is incremented by\n           the number of days specified in the setting\n           ``ACCOUNT_ACTIVATION_DAYS`` (which should be the number of\n           days after signup during which a user is allowed to\n           activate their account); if the result is less than or\n           equal to the current date, the key has expired and this\n           method returns ``True``.\n        \n        \"\"\"\n        expiration_date = datetime.timedelta(days=settings.ACCOUNT_ACTIVATION_DAYS)\n        return self.activation_key == self.ACTIVATED or \\\n               (self.user.date_joined + expiration_date <= datetime.datetime.now())\n    activation_key_expired.boolean = True\n"
  },
  {
    "path": "dirigible/registration/tests.py",
    "content": "\"\"\"\nUnit tests for django-registration.\n\nThese tests assume that you've completed all the prerequisites for\ngetting django-registration running in the default setup, to wit:\n\n1. You have ``registration`` in your ``INSTALLED_APPS`` setting.\n\n2. You have created all of the templates mentioned in this\n   application's documentation.\n\n3. You have added the setting ``ACCOUNT_ACTIVATION_DAYS`` to your\n   settings file.\n\n4. You have URL patterns pointing to the registration and activation\n   views, with the names ``registration_register`` and\n   ``registration_activate``, respectively, and a URL pattern named\n   'registration_complete'.\n\n\"\"\"\n\nimport datetime\nimport sha\n\nfrom django.conf import settings\nfrom django.contrib.auth.models import User\nfrom django.core import mail\nfrom django.core import management\nfrom django.core.urlresolvers import reverse\nfrom django.test import TestCase\nfrom django.conf import settings\n\nfrom registration import forms\nfrom registration.models import RegistrationProfile\n\n\nclass RegistrationTestCase(TestCase):\n    \"\"\"\n    Base class for the test cases; this sets up two users -- one\n    expired, one not -- which are used to exercise various parts of\n    the application.\n\n    \"\"\"\n    def setUp(self):\n        self.sample_user = RegistrationProfile.objects.create_inactive_user(username='alice',\n                                                                            password='secret',\n                                                                            email='alice@example.com')\n        self.expired_user = RegistrationProfile.objects.create_inactive_user(username='bob',\n                                                                             password='swordfish',\n                                                                             email='bob@example.com')\n        self.expired_user.date_joined -= datetime.timedelta(days=settings.ACCOUNT_ACTIVATION_DAYS + 1)\n        self.expired_user.save()\n\n\nclass RegistrationModelTests(RegistrationTestCase):\n    \"\"\"\n    Tests for the model-oriented functionality of django-registration,\n    including ``RegistrationProfile`` and its custom manager.\n\n    \"\"\"\n    def test_new_user_is_inactive(self):\n        \"\"\"\n        Test that a newly-created user is inactive.\n\n        \"\"\"\n        self.failIf(self.sample_user.is_active)\n\n    def test_registration_profile_created(self):\n        \"\"\"\n        Test that a ``RegistrationProfile`` is created for a new user.\n\n        \"\"\"\n        self.assertEqual(RegistrationProfile.objects.count(), 2)\n\n    def test_activation_email(self):\n        \"\"\"\n        Test that user signup sends an activation email.\n\n        \"\"\"\n        self.assertEqual(len(mail.outbox), 2)\n\n    def test_activation(self):\n        \"\"\"\n        Test that user activation actually activates the user and\n        properly resets the activation key, and fails for an\n        already-active or expired user, or an invalid key.\n\n        \"\"\"\n        # Activating a valid user returns the user.\n        self.failUnlessEqual(RegistrationProfile.objects.activate_user(RegistrationProfile.objects.get(user=self.sample_user).activation_key).pk,\n                             self.sample_user.pk)\n\n        # The activated user must now be active.\n        self.failUnless(User.objects.get(pk=self.sample_user.pk).is_active)\n\n        # The activation key must now be reset to the \"already activated\" constant.\n        self.failUnlessEqual(RegistrationProfile.objects.get(user=self.sample_user).activation_key,\n                             RegistrationProfile.ACTIVATED)\n\n        # Activating an expired user returns False.\n        self.failIf(RegistrationProfile.objects.activate_user(RegistrationProfile.objects.get(user=self.expired_user).activation_key))\n\n        # Activating from a key that isn't a SHA1 hash returns False.\n        self.failIf(RegistrationProfile.objects.activate_user('foo'))\n\n        # Activating from a key that doesn't exist returns False.\n        self.failIf(RegistrationProfile.objects.activate_user(sha.new('foo').hexdigest()))\n\n    def test_account_expiration_condition(self):\n        \"\"\"\n        Test that ``RegistrationProfile.activation_key_expired()``\n        returns ``True`` for expired users and for active users, and\n        ``False`` otherwise.\n\n        \"\"\"\n        # Unexpired user returns False.\n        self.failIf(RegistrationProfile.objects.get(user=self.sample_user).activation_key_expired())\n\n        # Expired user returns True.\n        self.failUnless(RegistrationProfile.objects.get(user=self.expired_user).activation_key_expired())\n\n        # Activated user returns True.\n        RegistrationProfile.objects.activate_user(RegistrationProfile.objects.get(user=self.sample_user).activation_key)\n        self.failUnless(RegistrationProfile.objects.get(user=self.sample_user).activation_key_expired())\n\n    def test_expired_user_deletion(self):\n        \"\"\"\n        Test that\n        ``RegistrationProfile.objects.delete_expired_users()`` deletes\n        only inactive users whose activation window has expired.\n\n        \"\"\"\n        RegistrationProfile.objects.delete_expired_users()\n        self.assertEqual(RegistrationProfile.objects.count(), 1)\n\n    def test_management_command(self):\n        \"\"\"\n        Test that ``manage.py cleanupregistration`` functions\n        correctly.\n\n        \"\"\"\n        management.call_command('cleanupregistration')\n        self.assertEqual(RegistrationProfile.objects.count(), 1)\n\n\nclass RegistrationFormTests(RegistrationTestCase):\n    \"\"\"\n    Tests for the forms and custom validation logic included in\n    django-registration.\n\n    \"\"\"\n    def test_registration_form(self):\n        \"\"\"\n        Test that ``RegistrationForm`` enforces username constraints\n        and matching passwords.\n\n        \"\"\"\n        invalid_data_dicts = [\n            # Non-alphanumeric username.\n            {\n            'data':\n            { 'username': 'foo/bar',\n              'email': 'foo@example.com',\n              'password1': 'foo',\n              'password2': 'foo' },\n            'error':\n            ('username', [u\"Enter a valid value.\"])\n            },\n            # Already-existing username.\n            {\n            'data':\n            { 'username': 'alice',\n              'email': 'alice@example.com',\n              'password1': 'secret',\n              'password2': 'secret' },\n            'error':\n            ('username', [u\"This username is already taken. Please choose another.\"])\n            },\n            # Mismatched passwords.\n            {\n            'data':\n            { 'username': 'foo',\n              'email': 'foo@example.com',\n              'password1': 'foo',\n              'password2': 'bar' },\n            'error':\n            ('__all__', [u\"You must type the same password each time\"])\n            },\n            ]\n\n        for invalid_dict in invalid_data_dicts:\n            form = forms.RegistrationForm(data=invalid_dict['data'])\n            self.failIf(form.is_valid())\n            self.assertEqual(form.errors[invalid_dict['error'][0]], invalid_dict['error'][1])\n\n        form = forms.RegistrationForm(data={ 'username': 'foo',\n                                             'email': 'foo@example.com',\n                                             'password1': 'foo',\n                                             'password2': 'foo' })\n        self.failUnless(form.is_valid())\n\n    def test_registration_form_tos(self):\n        \"\"\"\n        Test that ``RegistrationFormTermsOfService`` requires\n        agreement to the terms of service.\n\n        \"\"\"\n        form = forms.RegistrationFormTermsOfService(data={ 'username': 'foo',\n                                                           'email': 'foo@example.com',\n                                                           'password1': 'foo',\n                                                           'password2': 'foo' })\n        self.failIf(form.is_valid())\n        self.assertEqual(form.errors['tos'], [u\"You must agree to the terms to register\"])\n\n        form = forms.RegistrationFormTermsOfService(data={ 'username': 'foo',\n                                                           'email': 'foo@example.com',\n                                                           'password1': 'foo',\n                                                           'password2': 'foo',\n                                                           'tos': 'on' })\n        self.failUnless(form.is_valid())\n\n    def test_registration_form_unique_email(self):\n        \"\"\"\n        Test that ``RegistrationFormUniqueEmail`` validates uniqueness\n        of email addresses.\n\n        \"\"\"\n        form = forms.RegistrationFormUniqueEmail(data={ 'username': 'foo',\n                                                        'email': 'alice@example.com',\n                                                        'password1': 'foo',\n                                                        'password2': 'foo' })\n        self.failIf(form.is_valid())\n        self.assertEqual(form.errors['email'], [u\"This email address is already in use. Please supply a different email address.\"])\n\n        form = forms.RegistrationFormUniqueEmail(data={ 'username': 'foo',\n                                                        'email': 'foo@example.com',\n                                                        'password1': 'foo',\n                                                        'password2': 'foo' })\n        self.failUnless(form.is_valid())\n\n    def test_registration_form_no_free_email(self):\n        \"\"\"\n        Test that ``RegistrationFormNoFreeEmail`` disallows\n        registration with free email addresses.\n\n        \"\"\"\n        base_data = { 'username': 'foo',\n                      'password1': 'foo',\n                      'password2': 'foo' }\n        for domain in ('aim.com', 'aol.com', 'email.com', 'gmail.com',\n                       'googlemail.com', 'hotmail.com', 'hushmail.com',\n                       'msn.com', 'mail.ru', 'mailinator.com', 'live.com'):\n            invalid_data = base_data.copy()\n            invalid_data['email'] = u\"foo@%s\" % domain\n            form = forms.RegistrationFormNoFreeEmail(data=invalid_data)\n            self.failIf(form.is_valid())\n            self.assertEqual(form.errors['email'], [u\"Registration using free email addresses is prohibited. Please supply a different email address.\"])\n\n        base_data['email'] = 'foo@example.com'\n        form = forms.RegistrationFormNoFreeEmail(data=base_data)\n        self.failUnless(form.is_valid())\n\n\nclass RegistrationViewTests(RegistrationTestCase):\n    \"\"\"\n    Tests for the views included in django-registration.\n\n    \"\"\"\n    def test_registration_view(self):\n        \"\"\"\n        Test that the registration view rejects invalid submissions,\n        and creates a new user and redirects after a valid submission.\n\n        \"\"\"\n        # Invalid data fails.\n        response = self.client.post(reverse('registration_register'),\n                                    data={ 'username': 'alice', # Will fail on username uniqueness.\n                                           'email': 'foo@example.com',\n                                           'password1': 'foo',\n                                           'password2': 'foo' })\n        self.assertEqual(response.status_code, 200)\n        self.failUnless(response.context['form'])\n        self.failUnless(response.context['form'].errors)\n\n        response = self.client.post(reverse('registration_register'),\n                                    data={ 'username': 'foo',\n                                           'email': 'foo@example.com',\n                                           'password1': 'foo',\n                                           'password2': 'foo' })\n        self.assertEqual(response.status_code, 302)\n        self.assertEqual(response['Location'], 'http://testserver%s' % reverse('registration_complete'))\n        self.assertEqual(RegistrationProfile.objects.count(), 3)\n\n    def test_activation_view(self):\n        \"\"\"\n        Test that the activation view activates the user from a valid\n        key and fails if the key is invalid or has expired.\n\n        \"\"\"\n        # Valid user puts the user account into the context.\n        response = self.client.get(reverse('registration_activate',\n                                           kwargs={ 'activation_key': RegistrationProfile.objects.get(user=self.sample_user).activation_key }))\n        self.assertEqual(response.status_code, 200)\n        self.assertEqual(response.context['account'].pk, self.sample_user.pk)\n\n        # Expired user sets the account to False.\n        response = self.client.get(reverse('registration_activate',\n                                           kwargs={ 'activation_key': RegistrationProfile.objects.get(user=self.expired_user).activation_key }))\n        self.failIf(response.context['account'])\n\n        # Invalid key gets to the view, but sets account to False.\n        response = self.client.get(reverse('registration_activate',\n                                           kwargs={ 'activation_key': 'foo' }))\n        self.failIf(response.context['account'])\n\n        # Nonexistent key sets the account to False.\n        response = self.client.get(reverse('registration_activate',\n                                           kwargs={ 'activation_key': sha.new('foo').hexdigest() }))\n        self.failIf(response.context['account'])\n"
  },
  {
    "path": "dirigible/registration/urls.py",
    "content": "\"\"\"\nURLConf for Django user registration and authentication.\n\nIf the default behavior of the registration views is acceptable to\nyou, simply use a line like this in your root URLConf to set up the\ndefault URLs for registration::\n\n    (r'^accounts/', include('registration.urls')),\n\nThis will also automatically set up the views in\n``django.contrib.auth`` at sensible default locations.\n\nBut if you'd like to customize the behavior (e.g., by passing extra\narguments to the various views) or split up the URLs, feel free to set\nup your own URL patterns for these views instead. If you do, it's a\ngood idea to use the names ``registration_activate``,\n``registration_complete`` and ``registration_register`` for the\nvarious steps of the user-signup process.\n\n\"\"\"\n\n\nfrom django.conf.urls import patterns, url\nfrom django.contrib.auth import views as auth_views\nfrom django.views.generic import TemplateView\n\nfrom registration.views import activate\nfrom registration.views import register\n\n\nurlpatterns = patterns(\n    '',\n    # Activation keys get matched by \\w+ instead of the more specific\n    # [a-fA-F0-9]{40} because a bad activation key should still get to the view;\n    # that way it can return a sensible \"invalid key\" message instead of a\n    # confusing 404.\n    url(r'^activate/(?P<activation_key>\\w+)/$',\n        activate,\n        name='registration_activate'),\n    url(r'^login/$',\n        auth_views.login,\n        {'template_name': 'registration/login.html'},\n        name='auth_login'),\n    url(r'^logout/$',\n        auth_views.logout,\n        {'template_name': 'registration/logout.html'},\n        name='auth_logout'),\n    url(r'^password/change/$',\n        auth_views.password_change,\n        name='auth_password_change'),\n    url(r'^password/change/done/$',\n        auth_views.password_change_done,\n        name='auth_password_change_done'),\n    url(r'^password/reset/$',\n        auth_views.password_reset,\n        name='auth_password_reset'),\n    url(r'^password/reset/confirm/(?P<uidb36>[0-9A-Za-z]+)-(?P<token>.+)/$',\n        auth_views.password_reset_confirm,\n        name='auth_password_reset_confirm'),\n    url(r'^password/reset/complete/$',\n        auth_views.password_reset_complete,\n        name='auth_password_reset_complete'),\n    url(r'^password/reset/done/$',\n        auth_views.password_reset_done,\n        name='auth_password_reset_done'),\n    url(r'^register/$',\n        register,\n        name='registration_register'),\n    url(\n        r'^register/complete/$',\n        TemplateView.as_view(template='registration/registration_complete.html'),\n        name='registration_complete'\n    ),\n)\n"
  },
  {
    "path": "dirigible/registration/views.py",
    "content": "\"\"\"\nViews which allow users to create and activate accounts.\n\n\"\"\"\n\n\nfrom django.conf import settings\nfrom django.core.urlresolvers import reverse\nfrom django.http import HttpResponseRedirect\nfrom django.shortcuts import render_to_response\nfrom django.template import RequestContext\n\nfrom registration.forms import RegistrationForm\nfrom registration.models import RegistrationProfile\n\n\ndef activate(request, activation_key,\n             template_name='registration/activate.html',\n             extra_context=None):\n    \"\"\"\n    Activate a ``User``'s account from an activation key, if their key\n    is valid and hasn't expired.\n    \n    By default, use the template ``registration/activate.html``; to\n    change this, pass the name of a template as the keyword argument\n    ``template_name``.\n    \n    **Required arguments**\n    \n    ``activation_key``\n       The activation key to validate and use for activating the\n       ``User``.\n    \n    **Optional arguments**\n       \n    ``extra_context``\n        A dictionary of variables to add to the template context. Any\n        callable object in this dictionary will be called to produce\n        the end result which appears in the context.\n    \n    ``template_name``\n        A custom template to use.\n    \n    **Context:**\n    \n    ``account``\n        The ``User`` object corresponding to the account, if the\n        activation was successful. ``False`` if the activation was not\n        successful.\n    \n    ``expiration_days``\n        The number of days for which activation keys stay valid after\n        registration.\n    \n    Any extra variables supplied in the ``extra_context`` argument\n    (see above).\n    \n    **Template:**\n    \n    registration/activate.html or ``template_name`` keyword argument.\n    \n    \"\"\"\n    activation_key = activation_key.lower() # Normalize before trying anything with it.\n    account = RegistrationProfile.objects.activate_user(activation_key)\n    if extra_context is None:\n        extra_context = {}\n    context = RequestContext(request)\n    for key, value in extra_context.items():\n        context[key] = callable(value) and value() or value\n    return render_to_response(template_name,\n                              { 'account': account,\n                                'expiration_days': settings.ACCOUNT_ACTIVATION_DAYS },\n                              context_instance=context)\n\n\ndef register(request, success_url=None,\n             form_class=RegistrationForm, profile_callback=None,\n             template_name='registration/registration_form.html',\n             extra_context=None):\n    \"\"\"\n    Allow a new user to register an account.\n    \n    Following successful registration, issue a redirect; by default,\n    this will be whatever URL corresponds to the named URL pattern\n    ``registration_complete``, which will be\n    ``/accounts/register/complete/`` if using the included URLConf. To\n    change this, point that named pattern at another URL, or pass your\n    preferred URL as the keyword argument ``success_url``.\n    \n    By default, ``registration.forms.RegistrationForm`` will be used\n    as the registration form; to change this, pass a different form\n    class as the ``form_class`` keyword argument. The form class you\n    specify must have a method ``save`` which will create and return\n    the new ``User``, and that method must accept the keyword argument\n    ``profile_callback`` (see below).\n    \n    To enable creation of a site-specific user profile object for the\n    new user, pass a function which will create the profile object as\n    the keyword argument ``profile_callback``. See\n    ``RegistrationManager.create_inactive_user`` in the file\n    ``models.py`` for details on how to write this function.\n    \n    By default, use the template\n    ``registration/registration_form.html``; to change this, pass the\n    name of a template as the keyword argument ``template_name``.\n    \n    **Required arguments**\n    \n    None.\n    \n    **Optional arguments**\n    \n    ``form_class``\n        The form class to use for registration.\n    \n    ``extra_context``\n        A dictionary of variables to add to the template context. Any\n        callable object in this dictionary will be called to produce\n        the end result which appears in the context.\n    \n    ``profile_callback``\n        A function which will be used to create a site-specific\n        profile instance for the new ``User``.\n    \n    ``success_url``\n        The URL to redirect to on successful registration.\n    \n    ``template_name``\n        A custom template to use.\n    \n    **Context:**\n    \n    ``form``\n        The registration form.\n    \n    Any extra variables supplied in the ``extra_context`` argument\n    (see above).\n    \n    **Template:**\n    \n    registration/registration_form.html or ``template_name`` keyword\n    argument.\n    \n    \"\"\"\n    if request.method == 'POST':\n        form = form_class(data=request.POST, files=request.FILES)\n        if form.is_valid():\n            new_user = form.save(profile_callback=profile_callback)\n            # success_url needs to be dynamically generated here; setting a\n            # a default value using reverse() will cause circular-import\n            # problems with the default URLConf for this application, which\n            # imports this file.\n            return HttpResponseRedirect(success_url or reverse('registration_complete'))\n    else:\n        form = form_class()\n    \n    if extra_context is None:\n        extra_context = {}\n    context = RequestContext(request)\n    for key, value in extra_context.items():\n        context[key] = callable(value) and value() or value\n    return render_to_response(template_name,\n                              { 'form': form },\n                              context_instance=context)\n"
  },
  {
    "path": "dirigible/shared/__init__.py",
    "content": ""
  },
  {
    "path": "dirigible/shared/models.py",
    "content": "from django.db import models\n\n# Create your models here.\n"
  },
  {
    "path": "dirigible/shared/static/ace/ace-uncompressed.js",
    "content": "/* ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Ajax.org Code Editor (ACE).\n *\n * The Initial Developer of the Original Code is\n * Ajax.org B.V.\n * Portions created by the Initial Developer are Copyright (C) 2010\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *      Fabian Jakobs <fabian AT ajax DOT org>\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\n/**\n * Define a module along with a payload\n * @param module a name for the payload\n * @param payload a function to call with (require, exports, module) params\n */\n \n(function() {\n    \nif (window.require) {\n    require.packaged = true;\n    return;\n}\n    \nvar _define = function(module, deps, payload) {\n    if (typeof module !== 'string') {\n        if (_define.original)\n            _define.original.apply(window, arguments);\n        else {\n            console.error('dropping module because define wasn\\'t a string.');\n            console.trace();\n        }\n        return;\n    }\n\n    if (arguments.length == 2)\n        payload = deps;\n\n    if (!define.modules)\n        define.modules = {};\n        \n    define.modules[module] = payload;\n};\nif (window.define)\n    _define.original = window.define;\n    \nwindow.define = _define;\n\n\n/**\n * Get at functionality define()ed using the function above\n */\nvar _require = function(module, callback) {\n    if (Object.prototype.toString.call(module) === \"[object Array]\") {\n        var params = [];\n        for (var i = 0, l = module.length; i < l; ++i) {\n            var dep = lookup(module[i]);\n            if (!dep && _require.original)\n                return _require.original.apply(window, arguments);\n            params.push(dep);\n        }\n        if (callback) {\n            callback.apply(null, params);\n        }\n    }\n    else if (typeof module === 'string') {\n        var payload = lookup(module);\n        if (!payload && _require.original)\n            return _require.original.apply(window, arguments);\n        \n        if (callback) {\n            callback();\n        }\n    \n        return payload;\n    }\n    else {\n        if (_require.original)\n            return _require.original.apply(window, arguments);\n    }\n};\n\nif (window.require)\n    _require.original = window.require;\n    \nwindow.require = _require;\nrequire.packaged = true;\n\n/**\n * Internal function to lookup moduleNames and resolve them by calling the\n * definition function if needed.\n */\nvar lookup = function(moduleName) {\n    var module = define.modules[moduleName];\n    if (module == null) {\n        console.error('Missing module: ' + moduleName);\n        return null;\n    }\n\n    if (typeof module === 'function') {\n        var exports = {};\n        module(require, exports, { id: moduleName, uri: '' });\n        // cache the resulting module object for next time\n        define.modules[moduleName] = exports;\n        return exports;\n    }\n\n    return module;\n};\n\n})();// -- kriskowal Kris Kowal Copyright (C) 2009-2010 MIT License\n// -- tlrobinson Tom Robinson Copyright (C) 2009-2010 MIT License (Narwhal Project)\n// -- dantman Daniel Friesen Copyright(C) 2010 XXX No License Specified\n// -- fschaefer Florian Schäfer Copyright (C) 2010 MIT License\n\n/*!\n    Copyright (c) 2009, 280 North Inc. http://280north.com/\n    MIT License. http://github.com/280north/narwhal/blob/master/README.md\n*/\n\ndefine('pilot/fixoldbrowsers', function(require, exports, module) {\n\n/**\n * Brings an environment as close to ECMAScript 5 compliance\n * as is possible with the facilities of erstwhile engines.\n *\n * ES5 Draft\n * http://www.ecma-international.org/publications/files/drafts/tc39-2009-050.pdf\n *\n * NOTE: this is a draft, and as such, the URL is subject to change.  If the\n * link is broken, check in the parent directory for the latest TC39 PDF.\n * http://www.ecma-international.org/publications/files/drafts/\n *\n * Previous ES5 Draft\n * http://www.ecma-international.org/publications/files/drafts/tc39-2009-025.pdf\n * This is a broken link to the previous draft of ES5 on which most of the\n * numbered specification references and quotes herein were taken.  Updating\n * these references and quotes to reflect the new document would be a welcome\n * volunteer project.\n * \n * @module\n */\n\n/*whatsupdoc*/\n\n// this is often accessed, so avoid multiple dereference costs universally\nvar has = Object.prototype.hasOwnProperty;\n\n//\n// Array\n// =====\n//\n\n// ES5 15.4.3.2 \nif (!Array.isArray) {\n    Array.isArray = function(obj) {\n        return Object.prototype.toString.call(obj) == \"[object Array]\";\n    };\n}\n\n// ES5 15.4.4.18\nif (!Array.prototype.forEach) {\n    Array.prototype.forEach =  function(block, thisObject) {\n        var len = +this.length;\n        for (var i = 0; i < len; i++) {\n            if (i in this) {\n                block.call(thisObject, this[i], i, this);\n            }\n        }\n    };\n}\n\n// ES5 15.4.4.19\n// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/map\nif (!Array.prototype.map) {\n    Array.prototype.map = function(fun /*, thisp*/) {\n        var len = +this.length;\n        if (typeof fun != \"function\")\n          throw new TypeError();\n\n        var res = new Array(len);\n        var thisp = arguments[1];\n        for (var i = 0; i < len; i++) {\n            if (i in this)\n                res[i] = fun.call(thisp, this[i], i, this);\n        }\n\n        return res;\n    };\n}\n\n// ES5 15.4.4.20\nif (!Array.prototype.filter) {\n    Array.prototype.filter = function (block /*, thisp */) {\n        var values = [];\n        var thisp = arguments[1];\n        for (var i = 0; i < this.length; i++)\n            if (block.call(thisp, this[i]))\n                values.push(this[i]);\n        return values;\n    };\n}\n\n// ES5 15.4.4.16\nif (!Array.prototype.every) {\n    Array.prototype.every = function (block /*, thisp */) {\n        var thisp = arguments[1];\n        for (var i = 0; i < this.length; i++)\n            if (!block.call(thisp, this[i]))\n                return false;\n        return true;\n    };\n}\n\n// ES5 15.4.4.17\nif (!Array.prototype.some) {\n    Array.prototype.some = function (block /*, thisp */) {\n        var thisp = arguments[1];\n        for (var i = 0; i < this.length; i++)\n            if (block.call(thisp, this[i]))\n                return true;\n        return false;\n    };\n}\n\n// ES5 15.4.4.21\n// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/reduce\nif (!Array.prototype.reduce) {\n    Array.prototype.reduce = function(fun /*, initial*/) {\n        var len = +this.length;\n        if (typeof fun != \"function\")\n            throw new TypeError();\n\n        // no value to return if no initial value and an empty array\n        if (len == 0 && arguments.length == 1)\n            throw new TypeError();\n\n        var i = 0;\n        if (arguments.length >= 2) {\n            var rv = arguments[1];\n        } else {\n            do {\n                if (i in this) {\n                    rv = this[i++];\n                    break;\n                }\n\n                // if array contains no values, no initial value to return\n                if (++i >= len)\n                    throw new TypeError();\n            } while (true);\n        }\n\n        for (; i < len; i++) {\n            if (i in this)\n                rv = fun.call(null, rv, this[i], i, this);\n        }\n\n        return rv;\n    };\n}\n\n// ES5 15.4.4.22\n// https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/reduceRight\nif (!Array.prototype.reduceRight) {\n    Array.prototype.reduceRight = function(fun /*, initial*/) {\n        var len = +this.length;\n        if (typeof fun != \"function\")\n            throw new TypeError();\n\n        // no value to return if no initial value, empty array\n        if (len == 0 && arguments.length == 1)\n            throw new TypeError();\n\n        var i = len - 1;\n        if (arguments.length >= 2) {\n            var rv = arguments[1];\n        } else {\n            do {\n                if (i in this) {\n                    rv = this[i--];\n                    break;\n                }\n\n                // if array contains no values, no initial value to return\n                if (--i < 0)\n                    throw new TypeError();\n            } while (true);\n        }\n\n        for (; i >= 0; i--) {\n            if (i in this)\n                rv = fun.call(null, rv, this[i], i, this);\n        }\n\n        return rv;\n    };\n}\n\n// ES5 15.4.4.14\nif (!Array.prototype.indexOf) {\n    Array.prototype.indexOf = function (value /*, fromIndex */ ) {\n        var length = this.length;\n        if (!length)\n            return -1;\n        var i = arguments[1] || 0;\n        if (i >= length)\n            return -1;\n        if (i < 0)\n            i += length;\n        for (; i < length; i++) {\n            if (!has.call(this, i))\n                continue;\n            if (value === this[i])\n                return i;\n        }\n        return -1;\n    };\n}\n\n// ES5 15.4.4.15\nif (!Array.prototype.lastIndexOf) {\n    Array.prototype.lastIndexOf = function (value /*, fromIndex */) {\n        var length = this.length;\n        if (!length)\n            return -1;\n        var i = arguments[1] || length;\n        if (i < 0)\n            i += length;\n        i = Math.min(i, length - 1);\n        for (; i >= 0; i--) {\n            if (!has.call(this, i))\n                continue;\n            if (value === this[i])\n                return i;\n        }\n        return -1;\n    };\n}\n\n//\n// Object\n// ======\n// \n\n// ES5 15.2.3.2\nif (!Object.getPrototypeOf) {\n    // https://github.com/kriskowal/es5-shim/issues#issue/2\n    // http://ejohn.org/blog/objectgetprototypeof/\n    // recommended by fschaefer on github\n    Object.getPrototypeOf = function (object) {\n        return object.__proto__ || object.constructor.prototype;\n        // or undefined if not available in this engine\n    };\n}\n\n// ES5 15.2.3.3\nif (!Object.getOwnPropertyDescriptor) {\n    Object.getOwnPropertyDescriptor = function (object, property) {\n        if (typeof object !== \"object\" && typeof object !== \"function\" || object === null)\n            throw new TypeError(\"Object.getOwnPropertyDescriptor called on a non-object\");\n\n        return has.call(object, property) ? {\n            value: object[property],\n            enumerable: true,\n            configurable: true,\n            writeable: true\n        } : undefined;\n    };\n}\n\n// ES5 15.2.3.4\nif (!Object.getOwnPropertyNames) {\n    Object.getOwnPropertyNames = function (object) {\n        return Object.keys(object);\n    };\n}\n\n// ES5 15.2.3.5 \nif (!Object.create) {\n    Object.create = function(prototype, properties) {\n        var object;\n        if (prototype === null) {\n            object = {\"__proto__\": null};\n        } else {\n            if (typeof prototype != \"object\")\n                throw new TypeError(\"typeof prototype[\"+(typeof prototype)+\"] != 'object'\");\n            var Type = function () {};\n            Type.prototype = prototype;\n            object = new Type();\n        }\n        if (typeof properties !== \"undefined\")\n            Object.defineProperties(object, properties);\n        return object;\n    };\n}\n\n// ES5 15.2.3.6\nif (!Object.defineProperty) {\n    Object.defineProperty = function(object, property, descriptor) {\n        if (typeof descriptor == \"object\" && object.__defineGetter__) {\n            if (has.call(descriptor, \"value\")) {\n                if (!object.__lookupGetter__(property) && !object.__lookupSetter__(property))\n                    // data property defined and no pre-existing accessors\n                    object[property] = descriptor.value;\n                if (has.call(descriptor, \"get\") || has.call(descriptor, \"set\"))\n                    // descriptor has a value property but accessor already exists\n                    throw new TypeError(\"Object doesn't support this action\");\n            }\n            // fail silently if \"writable\", \"enumerable\", or \"configurable\"\n            // are requested but not supported\n            /*\n            // alternate approach:\n            if ( // can't implement these features; allow false but not true\n                !(has.call(descriptor, \"writable\") ? descriptor.writable : true) ||\n                !(has.call(descriptor, \"enumerable\") ? descriptor.enumerable : true) ||\n                !(has.call(descriptor, \"configurable\") ? descriptor.configurable : true)\n            )\n                throw new RangeError(\n                    \"This implementation of Object.defineProperty does not \" +\n                    \"support configurable, enumerable, or writable.\"\n                );\n            */\n            else if (typeof descriptor.get == \"function\")\n                object.__defineGetter__(property, descriptor.get);\n            if (typeof descriptor.set == \"function\")\n                object.__defineSetter__(property, descriptor.set);\n        }\n        return object;\n    };\n}\n\n// ES5 15.2.3.7\nif (!Object.defineProperties) {\n    Object.defineProperties = function(object, properties) {\n        for (var property in properties) {\n            if (has.call(properties, property))\n                Object.defineProperty(object, property, properties[property]);\n        }\n        return object;\n    };\n}\n\n// ES5 15.2.3.8\nif (!Object.seal) {\n    Object.seal = function (object) {\n        // this is misleading and breaks feature-detection, but\n        // allows \"securable\" code to \"gracefully\" degrade to working\n        // but insecure code.\n        return object;\n    };\n}\n\n// ES5 15.2.3.9\nif (!Object.freeze) {\n    Object.freeze = function (object) {\n        // this is misleading and breaks feature-detection, but\n        // allows \"securable\" code to \"gracefully\" degrade to working\n        // but insecure code.\n        return object;\n    };\n}\n\n// detect a Rhino bug and patch it\ntry {\n    Object.freeze(function () {});\n} catch (exception) {\n    Object.freeze = (function (freeze) {\n        return function (object) {\n            if (typeof object == \"function\") {\n                return object;\n            } else {\n                return freeze(object);\n            }\n        };\n    })(Object.freeze);\n}\n\n// ES5 15.2.3.10\nif (!Object.preventExtensions) {\n    Object.preventExtensions = function (object) {\n        // this is misleading and breaks feature-detection, but\n        // allows \"securable\" code to \"gracefully\" degrade to working\n        // but insecure code.\n        return object;\n    };\n}\n\n// ES5 15.2.3.11\nif (!Object.isSealed) {\n    Object.isSealed = function (object) {\n        return false;\n    };\n}\n\n// ES5 15.2.3.12\nif (!Object.isFrozen) {\n    Object.isFrozen = function (object) {\n        return false;\n    };\n}\n\n// ES5 15.2.3.13\nif (!Object.isExtensible) {\n    Object.isExtensible = function (object) {\n        return true;\n    };\n}\n\n// ES5 15.2.3.14\n// http://whattheheadsaid.com/2010/10/a-safer-object-keys-compatibility-implementation\nif (!Object.keys) {\n\n    var hasDontEnumBug = true,\n        dontEnums = [\n            'toString',\n            'toLocaleString',\n            'valueOf',\n            'hasOwnProperty',\n            'isPrototypeOf',\n            'propertyIsEnumerable',\n            'constructor'\n        ],\n        dontEnumsLength = dontEnums.length;\n\n    for (var key in {\"toString\": null})\n        hasDontEnumBug = false;\n\n    Object.keys = function (object) {\n\n        if (\n            typeof object !== \"object\" && typeof object !== \"function\"\n            || object === null\n        )\n            throw new TypeError(\"Object.keys called on a non-object\");\n\n        var keys = [];\n        for (var name in object) {\n            if (has.call(object, name)) {\n                keys.push(name);\n            }\n        }\n\n        if (hasDontEnumBug) {\n            for (var i = 0, ii = dontEnumsLength; i < ii; i++) {\n                var dontEnum = dontEnums[i];\n                if (has.call(object, dontEnum)) {\n                    keys.push(dontEnum);\n                }\n            }\n        }\n\n        return keys;\n    };\n\n}\n\n//\n// Date\n// ====\n//\n\n// ES5 15.9.5.43\n// Format a Date object as a string according to a subset of the ISO-8601 standard.\n// Useful in Atom, among other things.\nif (!Date.prototype.toISOString) {\n    Date.prototype.toISOString = function() {\n        return (\n            this.getUTCFullYear() + \"-\" +\n            (this.getUTCMonth() + 1) + \"-\" +\n            this.getUTCDate() + \"T\" +\n            this.getUTCHours() + \":\" +\n            this.getUTCMinutes() + \":\" +\n            this.getUTCSeconds() + \"Z\"\n        ); \n    }\n}\n\n// ES5 15.9.4.4\nif (!Date.now) {\n    Date.now = function () {\n        return new Date().getTime();\n    };\n}\n\n// ES5 15.9.5.44\nif (!Date.prototype.toJSON) {\n    Date.prototype.toJSON = function (key) {\n        // This function provides a String representation of a Date object for\n        // use by JSON.stringify (15.12.3). When the toJSON method is called\n        // with argument key, the following steps are taken:\n\n        // 1.  Let O be the result of calling ToObject, giving it the this\n        // value as its argument.\n        // 2. Let tv be ToPrimitive(O, hint Number).\n        // 3. If tv is a Number and is not finite, return null.\n        // XXX\n        // 4. Let toISO be the result of calling the [[Get]] internal method of\n        // O with argument \"toISOString\".\n        // 5. If IsCallable(toISO) is false, throw a TypeError exception.\n        if (typeof this.toISOString != \"function\")\n            throw new TypeError();\n        // 6. Return the result of calling the [[Call]] internal method of\n        // toISO with O as the this value and an empty argument list.\n        return this.toISOString();\n\n        // NOTE 1 The argument is ignored.\n\n        // NOTE 2 The toJSON function is intentionally generic; it does not\n        // require that its this value be a Date object. Therefore, it can be\n        // transferred to other kinds of objects for use as a method. However,\n        // it does require that any such object have a toISOString method. An\n        // object is free to use the argument key to filter its\n        // stringification.\n    };\n}\n\n// 15.9.4.2 Date.parse (string)\n// 15.9.1.15 Date Time String Format\n// Date.parse\n// based on work shared by Daniel Friesen (dantman)\n// http://gist.github.com/303249\nif (isNaN(Date.parse(\"T00:00\"))) {\n    // XXX global assignment won't work in embeddings that use\n    // an alternate object for the context.\n    Date = (function(NativeDate) {\n\n        // Date.length === 7\n        var Date = function(Y, M, D, h, m, s, ms) {\n            var length = arguments.length;\n            if (this instanceof NativeDate) {\n                var date = length === 1 && String(Y) === Y ? // isString(Y)\n                    // We explicitly pass it through parse:\n                    new NativeDate(Date.parse(Y)) :\n                    // We have to manually make calls depending on argument\n                    // length here\n                    length >= 7 ? new NativeDate(Y, M, D, h, m, s, ms) :\n                    length >= 6 ? new NativeDate(Y, M, D, h, m, s) :\n                    length >= 5 ? new NativeDate(Y, M, D, h, m) :\n                    length >= 4 ? new NativeDate(Y, M, D, h) :\n                    length >= 3 ? new NativeDate(Y, M, D) :\n                    length >= 2 ? new NativeDate(Y, M) :\n                    length >= 1 ? new NativeDate(Y) :\n                                  new NativeDate();\n                // Prevent mixups with unfixed Date object\n                date.constructor = Date;\n                return date;\n            }\n            return NativeDate.apply(this, arguments);\n        };\n\n        // 15.9.1.15 Date Time String Format\n        var isoDateExpression = new RegExp(\"^\" +\n            \"(?:\" + // optional year-month-day\n                \"(\" + // year capture\n                    \"(?:[+-]\\\\d\\\\d)?\" + // 15.9.1.15.1 Extended years\n                    \"\\\\d\\\\d\\\\d\\\\d\" + // four-digit year\n                \")\" +\n                \"(?:-\" + // optional month-day\n                    \"(\\\\d\\\\d)\" + // month capture\n                    \"(?:-\" + // optional day\n                        \"(\\\\d\\\\d)\" + // day capture\n                    \")?\" +\n                \")?\" +\n            \")?\" + \n            \"(?:T\" + // hour:minute:second.subsecond\n                \"(\\\\d\\\\d)\" + // hour capture\n                \":(\\\\d\\\\d)\" + // minute capture\n                \"(?::\" + // optional :second.subsecond\n                    \"(\\\\d\\\\d)\" + // second capture\n                    \"(?:\\\\.(\\\\d\\\\d\\\\d))?\" + // milisecond capture\n                \")?\" +\n            \")?\" +\n            \"(?:\" + // time zone\n                \"Z|\" + // UTC capture\n                \"([+-])(\\\\d\\\\d):(\\\\d\\\\d)\" + // timezone offset\n                // capture sign, hour, minute\n            \")?\" +\n        \"$\");\n\n        // Copy any custom methods a 3rd party library may have added\n        for (var key in NativeDate)\n            Date[key] = NativeDate[key];\n\n        // Copy \"native\" methods explicitly; they may be non-enumerable\n        Date.now = NativeDate.now;\n        Date.UTC = NativeDate.UTC;\n        Date.prototype = NativeDate.prototype;\n        Date.prototype.constructor = Date;\n\n        // Upgrade Date.parse to handle the ISO dates we use\n        // TODO review specification to ascertain whether it is\n        // necessary to implement partial ISO date strings.\n        Date.parse = function(string) {\n            var match = isoDateExpression.exec(string);\n            if (match) {\n                match.shift(); // kill match[0], the full match\n                // recognize times without dates before normalizing the\n                // numeric values, for later use\n                var timeOnly = match[0] === undefined;\n                // parse numerics\n                for (var i = 0; i < 10; i++) {\n                    // skip + or - for the timezone offset\n                    if (i === 7)\n                        continue;\n                    // Note: parseInt would read 0-prefix numbers as\n                    // octal.  Number constructor or unary + work better\n                    // here:\n                    match[i] = +(match[i] || (i < 3 ? 1 : 0));\n                    // match[1] is the month. Months are 0-11 in JavaScript\n                    // Date objects, but 1-12 in ISO notation, so we\n                    // decrement.\n                    if (i === 1)\n                        match[i]--;\n                }\n                // if no year-month-date is provided, return a milisecond\n                // quantity instead of a UTC date number value.\n                if (timeOnly)\n                    return ((match[3] * 60 + match[4]) * 60 + match[5]) * 1000 + match[6];\n\n                // account for an explicit time zone offset if provided\n                var offset = (match[8] * 60 + match[9]) * 60 * 1000;\n                if (match[6] === \"-\")\n                    offset = -offset;\n\n                return NativeDate.UTC.apply(this, match.slice(0, 7)) + offset;\n            }\n            return NativeDate.parse.apply(this, arguments);\n        };\n\n        return Date;\n    })(Date);\n}\n\n// \n// Function\n// ========\n// \n\n// ES-5 15.3.4.5\n// http://www.ecma-international.org/publications/files/drafts/tc39-2009-025.pdf\nvar slice = Array.prototype.slice;\nif (!Function.prototype.bind) {\n    Function.prototype.bind = function (that) { // .length is 1\n        // 1. Let Target be the this value.\n        var target = this;\n        // 2. If IsCallable(Target) is false, throw a TypeError exception.\n        // XXX this gets pretty close, for all intents and purposes, letting \n        // some duck-types slide\n        if (typeof target.apply != \"function\" || typeof target.call != \"function\")\n            return new TypeError();\n        // 3. Let A be a new (possibly empty) internal list of all of the\n        //   argument values provided after thisArg (arg1, arg2 etc), in order.\n        var args = slice.call(arguments);\n        // 4. Let F be a new native ECMAScript object.\n        // 9. Set the [[Prototype]] internal property of F to the standard\n        //   built-in Function prototype object as specified in 15.3.3.1.\n        // 10. Set the [[Call]] internal property of F as described in\n        //   15.3.4.5.1.\n        // 11. Set the [[Construct]] internal property of F as described in\n        //   15.3.4.5.2.\n        // 12. Set the [[HasInstance]] internal property of F as described in\n        //   15.3.4.5.3.\n        // 13. The [[Scope]] internal property of F is unused and need not\n        //   exist.\n        var bound = function () {\n\n            if (this instanceof bound) {\n                // 15.3.4.5.2 [[Construct]]\n                // When the [[Construct]] internal method of a function object,\n                // F that was created using the bind function is called with a\n                // list of arguments ExtraArgs the following steps are taken:\n                // 1. Let target be the value of F's [[TargetFunction]]\n                //   internal property.\n                // 2. If target has no [[Construct]] internal method, a\n                //   TypeError exception is thrown.\n                // 3. Let boundArgs be the value of F's [[BoundArgs]] internal\n                //   property.\n                // 4. Let args be a new list containing the same values as the\n                //   list boundArgs in the same order followed by the same\n                //   values as the list ExtraArgs in the same order.\n\n                var self = Object.create(target.prototype);\n                target.apply(self, args.concat(slice.call(arguments)));\n                return self;\n\n            } else {\n                // 15.3.4.5.1 [[Call]]\n                // When the [[Call]] internal method of a function object, F,\n                // which was created using the bind function is called with a\n                // this value and a list of arguments ExtraArgs the following\n                // steps are taken:\n                // 1. Let boundArgs be the value of F's [[BoundArgs]] internal\n                //   property.\n                // 2. Let boundThis be the value of F's [[BoundThis]] internal\n                //   property.\n                // 3. Let target be the value of F's [[TargetFunction]] internal\n                //   property.\n                // 4. Let args be a new list containing the same values as the list\n                //   boundArgs in the same order followed by the same values as\n                //   the list ExtraArgs in the same order. 5.  Return the\n                //   result of calling the [[Call]] internal method of target\n                //   providing boundThis as the this value and providing args\n                //   as the arguments.\n\n                // equiv: target.call(this, ...boundArgs, ...args)\n                return target.call.apply(\n                    target,\n                    args.concat(slice.call(arguments))\n                );\n\n            }\n\n        };\n        // 5. Set the [[TargetFunction]] internal property of F to Target.\n        // extra:\n        bound.bound = target;\n        // 6. Set the [[BoundThis]] internal property of F to the value of\n        // thisArg.\n        // extra:\n        bound.boundTo = that;\n        // 7. Set the [[BoundArgs]] internal property of F to A.\n        // extra:\n        bound.boundArgs = args;\n        bound.length = (\n            // 14. If the [[Class]] internal property of Target is \"Function\", then\n            typeof target == \"function\" ?\n            // a. Let L be the length property of Target minus the length of A.\n            // b. Set the length own property of F to either 0 or L, whichever is larger.\n            Math.max(target.length - args.length, 0) :\n            // 15. Else set the length own property of F to 0.\n            0\n        )\n        // 16. The length own property of F is given attributes as specified in\n        //   15.3.5.1.\n        // TODO\n        // 17. Set the [[Extensible]] internal property of F to true.\n        // TODO\n        // 18. Call the [[DefineOwnProperty]] internal method of F with\n        //   arguments \"caller\", PropertyDescriptor {[[Value]]: null,\n        //   [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]:\n        //   false}, and false.\n        // TODO\n        // 19. Call the [[DefineOwnProperty]] internal method of F with\n        //   arguments \"arguments\", PropertyDescriptor {[[Value]]: null,\n        //   [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]:\n        //   false}, and false.\n        // TODO\n        // NOTE Function objects created using Function.prototype.bind do not\n        // have a prototype property.\n        // XXX can't delete it in pure-js.\n        return bound;\n    };\n}\n\n//\n// String\n// ======\n//\n\n// ES5 15.5.4.20\nif (!String.prototype.trim) {\n    // http://blog.stevenlevithan.com/archives/faster-trim-javascript\n    var trimBeginRegexp = /^\\s\\s*/;\n    var trimEndRegexp = /\\s\\s*$/;\n    String.prototype.trim = function () {\n        return String(this).replace(trimBeginRegexp, '').replace(trimEndRegexp, '');\n    };\n}\n\nexports.globalsLoaded = true;\n\n});/* ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Mozilla Skywriter.\n *\n * The Initial Developer of the Original Code is\n * Mozilla.\n * Portions created by the Initial Developer are Copyright (C) 2009\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *      Kevin Dangoor (kdangoor@mozilla.com)\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('pilot/index', function(require, exports, module) {\n\nexports.startup = function(data, reason) {\n    require('pilot/fixoldbrowsers');\n\n    require('pilot/types/basic').startup(data, reason);\n    require('pilot/types/command').startup(data, reason);\n    require('pilot/types/settings').startup(data, reason);\n    require('pilot/commands/settings').startup(data, reason);\n    require('pilot/commands/basic').startup(data, reason);\n    // require('pilot/commands/history').startup(data, reason);\n    require('pilot/settings/canon').startup(data, reason);\n    require('pilot/canon').startup(data, reason);\n};\n\nexports.shutdown = function(data, reason) {\n    require('pilot/types/basic').shutdown(data, reason);\n    require('pilot/types/command').shutdown(data, reason);\n    require('pilot/types/settings').shutdown(data, reason);\n    require('pilot/commands/settings').shutdown(data, reason);\n    require('pilot/commands/basic').shutdown(data, reason);\n    // require('pilot/commands/history').shutdown(data, reason);\n    require('pilot/settings/canon').shutdown(data, reason);\n    require('pilot/canon').shutdown(data, reason);\n};\n\n\n});\n/* ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Mozilla Skywriter.\n *\n * The Initial Developer of the Original Code is\n * Mozilla.\n * Portions created by the Initial Developer are Copyright (C) 2009\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *      Joe Walker (jwalker@mozilla.com)\n *      Kevin Dangoor (kdangoor@mozilla.com)\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('pilot/types/basic', function(require, exports, module) {\n\nvar types = require(\"pilot/types\");\nvar Type = types.Type;\nvar Conversion = types.Conversion;\nvar Status = types.Status;\n\n/**\n * These are the basic types that we accept. They are vaguely based on the\n * Jetpack settings system (https://wiki.mozilla.org/Labs/Jetpack/JEP/24)\n * although clearly more restricted.\n *\n * <p>In addition to these types, Jetpack also accepts range, member, password\n * that we are thinking of adding.\n *\n * <p>This module probably should not be accessed directly, but instead used\n * through types.js\n */\n\n/**\n * 'text' is the default if no type is given.\n */\nvar text = new Type();\n\ntext.stringify = function(value) {\n    return value;\n};\n\ntext.parse = function(value) {\n    if (typeof value != 'string') {\n        throw new Error('non-string passed to text.parse()');\n    }\n    return new Conversion(value);\n};\n\ntext.name = 'text';\n\n/**\n * We don't currently plan to distinguish between integers and floats\n */\nvar number = new Type();\n\nnumber.stringify = function(value) {\n    if (!value) {\n        return null;\n    }\n    return '' + value;\n};\n\nnumber.parse = function(value) {\n    if (typeof value != 'string') {\n        throw new Error('non-string passed to number.parse()');\n    }\n\n    if (value.replace(/\\s/g, '').length === 0) {\n        return new Conversion(null, Status.INCOMPLETE, '');\n    }\n\n    var reply = new Conversion(parseInt(value, 10));\n    if (isNaN(reply.value)) {\n        reply.status = Status.INVALID;\n        reply.message = 'Can\\'t convert \"' + value + '\" to a number.';\n    }\n\n    return reply;\n};\n\nnumber.decrement = function(value) {\n    return value - 1;\n};\n\nnumber.increment = function(value) {\n    return value + 1;\n};\n\nnumber.name = 'number';\n\n/**\n * One of a known set of options\n */\nfunction SelectionType(typeSpec) {\n    if (!Array.isArray(typeSpec.data) && typeof typeSpec.data !== 'function') {\n        throw new Error('instances of SelectionType need typeSpec.data to be an array or function that returns an array:' + JSON.stringify(typeSpec));\n    }\n    Object.keys(typeSpec).forEach(function(key) {\n        this[key] = typeSpec[key];\n    }, this);\n};\n\nSelectionType.prototype = new Type();\n\nSelectionType.prototype.stringify = function(value) {\n    return value;\n};\n\nSelectionType.prototype.parse = function(str) {\n    if (typeof str != 'string') {\n        throw new Error('non-string passed to parse()');\n    }\n    if (!this.data) {\n        throw new Error('Missing data on selection type extension.');\n    }\n    var data = (typeof(this.data) === 'function') ? this.data() : this.data;\n\n    // The matchedValue could be the boolean value false\n    var hasMatched = false;\n    var matchedValue;\n    var completions = [];\n    data.forEach(function(option) {\n        if (str == option) {\n            matchedValue = this.fromString(option);\n            hasMatched = true;\n        }\n        else if (option.indexOf(str) === 0) {\n            completions.push(this.fromString(option));\n        }\n    }, this);\n\n    if (hasMatched) {\n        return new Conversion(matchedValue);\n    }\n    else {\n        // This is something of a hack it basically allows us to tell the\n        // setting type to forget its last setting hack.\n        if (this.noMatch) {\n            this.noMatch();\n        }\n\n        if (completions.length > 0) {\n            var msg = 'Possibilities' +\n                (str.length === 0 ? '' : ' for \\'' + str + '\\'');\n            return new Conversion(null, Status.INCOMPLETE, msg, completions);\n        }\n        else {\n            var msg = 'Can\\'t use \\'' + str + '\\'.';\n            return new Conversion(null, Status.INVALID, msg, completions);\n        }\n    }\n};\n\nSelectionType.prototype.fromString = function(str) {\n    return str;\n};\n\nSelectionType.prototype.decrement = function(value) {\n    var data = (typeof this.data === 'function') ? this.data() : this.data;\n    var index;\n    if (value == null) {\n        index = data.length - 1;\n    }\n    else {\n        var name = this.stringify(value);\n        var index = data.indexOf(name);\n        index = (index === 0 ? data.length - 1 : index - 1);\n    }\n    return this.fromString(data[index]);\n};\n\nSelectionType.prototype.increment = function(value) {\n    var data = (typeof this.data === 'function') ? this.data() : this.data;\n    var index;\n    if (value == null) {\n        index = 0;\n    }\n    else {\n        var name = this.stringify(value);\n        var index = data.indexOf(name);\n        index = (index === data.length - 1 ? 0 : index + 1);\n    }\n    return this.fromString(data[index]);\n};\n\nSelectionType.prototype.name = 'selection';\n\n/**\n * SelectionType is a base class for other types\n */\nexports.SelectionType = SelectionType;\n\n/**\n * true/false values\n */\nvar bool = new SelectionType({\n    name: 'bool',\n    data: [ 'true', 'false' ],\n    stringify: function(value) {\n        return '' + value;\n    },\n    fromString: function(str) {\n        return str === 'true' ? true : false;\n    }\n});\n\n\n/**\n * A we don't know right now, but hope to soon.\n */\nfunction DeferredType(typeSpec) {\n    if (typeof typeSpec.defer !== 'function') {\n        throw new Error('Instances of DeferredType need typeSpec.defer to be a function that returns a type');\n    }\n    Object.keys(typeSpec).forEach(function(key) {\n        this[key] = typeSpec[key];\n    }, this);\n};\n\nDeferredType.prototype = new Type();\n\nDeferredType.prototype.stringify = function(value) {\n    return this.defer().stringify(value);\n};\n\nDeferredType.prototype.parse = function(value) {\n    return this.defer().parse(value);\n};\n\nDeferredType.prototype.decrement = function(value) {\n    var deferred = this.defer();\n    return (deferred.decrement ? deferred.decrement(value) : undefined);\n};\n\nDeferredType.prototype.increment = function(value) {\n    var deferred = this.defer();\n    return (deferred.increment ? deferred.increment(value) : undefined);\n};\n\nDeferredType.prototype.name = 'deferred';\n\n/**\n * DeferredType is a base class for other types\n */\nexports.DeferredType = DeferredType;\n\n\n/**\n * A set of objects of the same type\n */\nfunction ArrayType(typeSpec) {\n    if (typeSpec instanceof Type) {\n        this.subtype = typeSpec;\n    }\n    else if (typeof typeSpec === 'string') {\n        this.subtype = types.getType(typeSpec);\n        if (this.subtype == null) {\n            throw new Error('Unknown array subtype: ' + typeSpec);\n        }\n    }\n    else {\n        throw new Error('Can\\' handle array subtype');\n    }\n};\n\nArrayType.prototype = new Type();\n\nArrayType.prototype.stringify = function(values) {\n    // TODO: Check for strings with spaces and add quotes\n    return values.join(' ');\n};\n\nArrayType.prototype.parse = function(value) {\n    return this.defer().parse(value);\n};\n\nArrayType.prototype.name = 'array';\n\n/**\n * Registration and de-registration.\n */\nvar isStarted = false;\nexports.startup = function() {\n    if (isStarted) {\n        return;\n    }\n    isStarted = true;\n    types.registerType(text);\n    types.registerType(number);\n    types.registerType(bool);\n    types.registerType(SelectionType);\n    types.registerType(DeferredType);\n    types.registerType(ArrayType);\n};\n\nexports.shutdown = function() {\n    isStarted = false;\n    types.unregisterType(text);\n    types.unregisterType(number);\n    types.unregisterType(bool);\n    types.unregisterType(SelectionType);\n    types.unregisterType(DeferredType);\n    types.unregisterType(ArrayType);\n};\n\n\n});\n/* ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Skywriter.\n *\n * The Initial Developer of the Original Code is\n * Mozilla.\n * Portions created by the Initial Developer are Copyright (C) 2009\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *   Joe Walker (jwalker@mozilla.com)\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('pilot/types', function(require, exports, module) {\n\n/**\n * Some types can detect validity, that is to say they can distinguish between\n * valid and invalid values.\n * TODO: Change these constants to be numbers for more performance?\n */\nvar Status = {\n    /**\n     * The conversion process worked without any problem, and the value is\n     * valid. There are a number of failure states, so the best way to check\n     * for failure is (x !== Status.VALID)\n     */\n    VALID: {\n        toString: function() { return 'VALID'; },\n        valueOf: function() { return 0; }\n    },\n\n    /**\n     * A conversion process failed, however it was noted that the string\n     * provided to 'parse()' could be VALID by the addition of more characters,\n     * so the typing may not be actually incorrect yet, just unfinished.\n     * @see Status.INVALID\n     */\n    INCOMPLETE: {\n        toString: function() { return 'INCOMPLETE'; },\n        valueOf: function() { return 1; }\n    },\n\n    /**\n     * The conversion process did not work, the value should be null and a\n     * reason for failure should have been provided. In addition some completion\n     * values may be available.\n     * @see Status.INCOMPLETE\n     */\n    INVALID: {\n        toString: function() { return 'INVALID'; },\n        valueOf: function() { return 2; }\n    },\n\n    /**\n     * A combined status is the worser of the provided statuses\n     */\n    combine: function(statuses) {\n        var combined = Status.VALID;\n        for (var i = 0; i < statuses.length; i++) {\n            if (statuses[i].valueOf() > combined.valueOf()) {\n                combined = statuses[i];\n            }\n        }\n        return combined;\n    }\n};\nexports.Status = Status;\n\n/**\n * The type.parse() method returns a Conversion to inform the user about not\n * only the result of a Conversion but also about what went wrong.\n * We could use an exception, and throw if the conversion failed, but that\n * seems to violate the idea that exceptions should be exceptional. Typos are\n * not. Also in order to store both a status and a message we'd still need\n * some sort of exception type...\n */\nfunction Conversion(value, status, message, predictions) {\n    /**\n     * The result of the conversion process. Will be null if status != VALID\n     */\n    this.value = value;\n\n    /**\n     * The status of the conversion.\n     * @see Status\n     */\n    this.status = status || Status.VALID;\n\n    /**\n     * A message to go with the conversion. This could be present for any status\n     * including VALID in the case where we want to note a warning for example.\n     * I18N: On the one hand this nasty and un-internationalized, however with\n     * a command line it is hard to know where to start.\n     */\n    this.message = message;\n\n    /**\n     * A array of strings which are the systems best guess at better inputs than\n     * the one presented.\n     * We generally expect there to be about 7 predictions (to match human list\n     * comprehension ability) however it is valid to provide up to about 20,\n     * or less. It is the job of the predictor to decide a smart cut-off.\n     * For example if there are 4 very good matches and 4 very poor ones,\n     * probably only the 4 very good matches should be presented.\n     */\n    this.predictions = predictions || [];\n}\nexports.Conversion = Conversion;\n\n/**\n * Most of our types are 'static' e.g. there is only one type of 'text', however\n * some types like 'selection' and 'deferred' are customizable. The basic\n * Type type isn't useful, but does provide documentation about what types do.\n */\nfunction Type() {\n};\nType.prototype = {\n    /**\n     * Convert the given <tt>value</tt> to a string representation.\n     * Where possible, there should be round-tripping between values and their\n     * string representations.\n     */\n    stringify: function(value) { throw new Error(\"not implemented\"); },\n\n    /**\n     * Convert the given <tt>str</tt> to an instance of this type.\n     * Where possible, there should be round-tripping between values and their\n     * string representations.\n     * @return Conversion\n     */\n    parse: function(str) { throw new Error(\"not implemented\"); },\n\n    /**\n     * The plug-in system, and other things need to know what this type is\n     * called. The name alone is not enough to fully specify a type. Types like\n     * 'selection' and 'deferred' need extra data, however this function returns\n     * only the name, not the extra data.\n     * <p>In old bespin, equality was based on the name. This may turn out to be\n     * important in Ace too.\n     */\n    name: undefined,\n\n    /**\n     * If there is some concept of a higher value, return it,\n     * otherwise return undefined.\n     */\n    increment: function(value) {\n        return undefined;\n    },\n\n    /**\n     * If there is some concept of a lower value, return it,\n     * otherwise return undefined.\n     */\n    decrement: function(value) {\n        return undefined;\n    },\n\n    /**\n     * There is interesting information (like predictions) in a conversion of\n     * nothing, the output of this can sometimes be customized.\n     * @return Conversion\n     */\n    getDefault: function() {\n        return this.parse('');\n    }\n};\nexports.Type = Type;\n\n/**\n * Private registry of types\n * Invariant: types[name] = type.name\n */\nvar types = {};\n\n/**\n * Add a new type to the list available to the system.\n * You can pass 2 things to this function - either an instance of Type, in\n * which case we return this instance when #getType() is called with a 'name'\n * that matches type.name.\n * Also you can pass in a constructor (i.e. function) in which case when\n * #getType() is called with a 'name' that matches Type.prototype.name we will\n * pass the typeSpec into this constructor. See #reconstituteType().\n */\nexports.registerType = function(type) {\n    if (typeof type === 'object') {\n        if (type instanceof Type) {\n            if (!type.name) {\n                throw new Error('All registered types must have a name');\n            }\n            types[type.name] = type;\n        }\n        else {\n            throw new Error('Can\\'t registerType using: ' + type);\n        }\n    }\n    else if (typeof type === 'function') {\n        if (!type.prototype.name) {\n            throw new Error('All registered types must have a name');\n        }\n        types[type.prototype.name] = type;\n    }\n    else {\n        throw new Error('Unknown type: ' + type);\n    }\n};\n\nexports.registerTypes = function registerTypes(types) {\n    Object.keys(types).forEach(function (name) {\n        var type = types[name];\n        type.name = name;\n        exports.registerType(type);\n    });\n};\n\n/**\n * Remove a type from the list available to the system\n */\nexports.deregisterType = function(type) {\n    delete types[type.name];\n};\n\n/**\n * See description of #exports.registerType()\n */\nfunction reconstituteType(name, typeSpec) {\n    if (name.substr(-2) === '[]') { // i.e. endsWith('[]')\n        var subtypeName = name.slice(0, -2);\n        return new types['array'](subtypeName);\n    }\n\n    var type = types[name];\n    if (typeof type === 'function') {\n        type = new type(typeSpec);\n    }\n    return type;\n}\n\n/**\n * Find a type, previously registered using #registerType()\n */\nexports.getType = function(typeSpec) {\n    if (typeof typeSpec === 'string') {\n        return reconstituteType(typeSpec);\n    }\n\n    if (typeof typeSpec === 'object') {\n        if (!typeSpec.name) {\n            throw new Error('Missing \\'name\\' member to typeSpec');\n        }\n        return reconstituteType(typeSpec.name, typeSpec);\n    }\n\n    throw new Error('Can\\'t extract type from ' + typeSpec);\n};\n\n\n});\n/* ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Mozilla Skywriter.\n *\n * The Initial Developer of the Original Code is\n * Mozilla.\n * Portions created by the Initial Developer are Copyright (C) 2009\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *      Joe Walker (jwalker@mozilla.com)\n *      Kevin Dangoor (kdangoor@mozilla.com)\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('pilot/types/command', function(require, exports, module) {\n\nvar canon = require(\"pilot/canon\");\nvar SelectionType = require(\"pilot/types/basic\").SelectionType;\nvar types = require(\"pilot/types\");\n\n\n/**\n * Select from the available commands\n */\nvar command = new SelectionType({\n    name: 'command',\n    data: function() {\n        return canon.getCommandNames();\n    },\n    stringify: function(command) {\n        return command.name;\n    },\n    fromString: function(str) {\n        return canon.getCommand(str);\n    }\n});\n\n\n/**\n * Registration and de-registration.\n */\nexports.startup = function() {\n    types.registerType(command);\n};\n\nexports.shutdown = function() {\n    types.unregisterType(command);\n};\n\n\n});\n/* ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Mozilla Skywriter.\n *\n * The Initial Developer of the Original Code is\n * Mozilla.\n * Portions created by the Initial Developer are Copyright (C) 2009\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *   Joe Walker (jwalker@mozilla.com)\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('pilot/canon', function(require, exports, module) {\n\nvar console = require('pilot/console');\nvar Trace = require('pilot/stacktrace').Trace;\nvar oop = require('pilot/oop');\nvar useragent = require('pilot/useragent');\nvar keyUtil = require('pilot/keys');\nvar EventEmitter = require('pilot/event_emitter').EventEmitter;\nvar typecheck = require('pilot/typecheck');\nvar catalog = require('pilot/catalog');\nvar Status = require('pilot/types').Status;\nvar types = require('pilot/types');\nvar lang = require('pilot/lang');\n\n/*\n// TODO: this doesn't belong here - or maybe anywhere?\nvar dimensionsChangedExtensionSpec = {\n    name: 'dimensionsChanged',\n    description: 'A dimensionsChanged is a way to be notified of ' +\n            'changes to the dimension of Skywriter'\n};\nexports.startup = function(data, reason) {\n    catalog.addExtensionSpec(commandExtensionSpec);\n};\nexports.shutdown = function(data, reason) {\n    catalog.removeExtensionSpec(commandExtensionSpec);\n};\n*/\n\nvar commandExtensionSpec = {\n    name: 'command',\n    description: 'A command is a bit of functionality with optional ' +\n            'typed arguments which can do something small like moving ' +\n            'the cursor around the screen, or large like cloning a ' +\n            'project from VCS.',\n    indexOn: 'name'\n};\n\nexports.startup = function(data, reason) {\n    // TODO: this is probably all kinds of evil, but we need something working\n    catalog.addExtensionSpec(commandExtensionSpec);\n};\n\nexports.shutdown = function(data, reason) {\n    catalog.removeExtensionSpec(commandExtensionSpec);\n};\n\n/**\n * Manage a list of commands in the current canon\n */\n\n/**\n * A Command is a discrete action optionally with a set of ways to customize\n * how it happens. This is here for documentation purposes.\n * TODO: Document better\n */\nvar thingCommand = {\n    name: 'thing',\n    description: 'thing is an example command',\n    params: [{\n        name: 'param1',\n        description: 'an example parameter',\n        type: 'text',\n        defaultValue: null\n    }],\n    exec: function(env, args, request) {\n        thing();\n    }\n};\n\n/**\n * A lookup hash of our registered commands\n */\nvar commands = {};\n\n/**\n * A lookup has for command key bindings that use a string as sender.\n */\nvar commmandKeyBinding = {};\n\n/**\n * Array with command key bindings that use a function to determ the sender.\n */\nvar commandKeyBindingFunc = { };\n\nfunction splitSafe(s, separator, limit, bLowerCase) {\n    return (bLowerCase && s.toLowerCase() || s)\n        .replace(/(?:^\\s+|\\n|\\s+$)/g, \"\")\n        .split(new RegExp(\"[\\\\s ]*\" + separator + \"[\\\\s ]*\", \"g\"), limit || 999);\n}\n\nfunction parseKeys(keys, val, ret) {\n    var key,\n        hashId = 0,\n        parts  = splitSafe(keys, \"\\\\-\", null, true),\n        i      = 0,\n        l      = parts.length;\n\n    for (; i < l; ++i) {\n        if (keyUtil.KEY_MODS[parts[i]])\n            hashId = hashId | keyUtil.KEY_MODS[parts[i]];\n        else\n            key = parts[i] || \"-\"; //when empty, the splitSafe removed a '-'\n    }\n\n    if (ret == null) {\n        return {\n            key: key,\n            hashId: hashId\n        }   \n    } else {\n        (ret[hashId] || (ret[hashId] = {}))[key] = val;\n    }\n}\n\nvar platform = useragent.isMac ? \"mac\" : \"win\";\nfunction buildKeyHash(command) {\n    var binding = command.bindKey,\n        key = binding[platform],\n        ckb = commmandKeyBinding,\n        ckbf = commandKeyBindingFunc\n\n    if (!binding.sender) {\n        throw new Error('All key bindings must have a sender');   \n    }\n    if (!binding.mac && binding.mac !== null) {\n        throw new Error('All key bindings must have a mac key binding');\n    }\n    if (!binding.win && binding.win !== null) {\n        throw new Error('All key bindings must have a windows key binding');\n    }\n    if(!binding[platform]) {\n        // No keymapping for this platform.\n        return;   \n    }\n    if (typeof binding.sender == 'string') {\n        var targets = splitSafe(binding.sender, \"\\\\|\", null, true);\n        targets.forEach(function(target) {\n            if (!ckb[target]) {\n                ckb[target] = { };\n            }\n            key.split(\"|\").forEach(function(keyPart) {\n                parseKeys(keyPart, command, ckb[target]);        \n            });\n        });\n    } else if (typecheck.isFunction(binding.sender)) {\n        var val = {\n            command: command,\n            sender:  binding.sender\n        };\n        \n        keyData = parseKeys(key);\n        if (!ckbf[keyData.hashId]) {\n            ckbf[keyData.hashId] = { };\n        }\n        if (!ckbf[keyData.hashId][keyData.key]) {\n            ckbf[keyData.hashId][keyData.key] = [ val ];   \n        } else {\n            ckbf[keyData.hashId][keyData.key].push(val);\n        }\n    } else {\n        throw new Error('Key binding must have a sender that is a string or function');   \n    }\n}\n\nfunction findKeyCommand(env, sender, hashId, textOrKey) {\n    // Convert keyCode to the string representation.\n    if (typecheck.isNumber(textOrKey)) {\n        textOrKey = keyUtil.keyCodeToString(textOrKey);\n    }\n    \n    // Check bindings with functions as sender first.    \n    var bindFuncs = (commandKeyBindingFunc[hashId]  || {})[textOrKey] || [];\n    for (var i = 0; i < bindFuncs.length; i++) {\n        if (bindFuncs[i].sender(env, sender, hashId, textOrKey)) {\n            return bindFuncs[i].command;\n        }\n    }\n    \n    var ckbr = commmandKeyBinding[sender]\n    return ckbr && ckbr[hashId] && ckbr[hashId][textOrKey];\n}\n\nfunction execKeyCommand(env, sender, hashId, textOrKey) {\n    var command = findKeyCommand(env, sender, hashId, textOrKey);\n    if (command) {\n        return exec(command, env, sender, { });   \n    } else {\n        return false;\n    }\n}\n\n/**\n * A sorted list of command names, we regularly want them in order, so pre-sort\n */\nvar commandNames = [];\n\n/**\n * This registration method isn't like other Ace registration methods because\n * it doesn't return a decorated command because there is no functional\n * decoration to be done.\n * TODO: Are we sure that in the future there will be no such decoration?\n */\nfunction addCommand(command) {\n    if (!command.name) {\n        throw new Error('All registered commands must have a name');\n    }\n    if (command.params == null) {\n        command.params = [];\n    }\n    if (!Array.isArray(command.params)) {\n        throw new Error('command.params must be an array in ' + command.name);\n    }\n    // Replace the type\n    command.params.forEach(function(param) {\n        if (!param.name) {\n            throw new Error('In ' + command.name + ': all params must have a name');\n        }\n        upgradeType(command.name, param);\n    }, this);\n    commands[command.name] = command;\n\n    if (command.bindKey) {\n        buildKeyHash(command);   \n    }\n\n    commandNames.push(command.name);\n    commandNames.sort();\n};\n\nfunction upgradeType(name, param) {\n    var lookup = param.type;\n    param.type = types.getType(lookup);\n    if (param.type == null) {\n        throw new Error('In ' + name + '/' + param.name +\n            ': can\\'t find type for: ' + JSON.stringify(lookup));\n    }\n}\n\nfunction removeCommand(command) {\n    var name = (typeof command === 'string' ? command : command.name);\n    delete commands[name];\n    lang.arrayRemove(commandNames, name);\n};\n\nfunction getCommand(name) {\n    return commands[name];\n};\n\nfunction getCommandNames() {\n    return commandNames;\n};\n\n/**\n * Default ArgumentProvider that is used if no ArgumentProvider is provided\n * by the command's sender.\n */\nfunction defaultArgsProvider(request, callback) {\n    var args  = request.args,\n        params = request.command.params;\n\n    for (var i = 0; i < params.length; i++) {\n        var param = params[i];\n\n        // If the parameter is already valid, then don't ask for it anymore.\n        if (request.getParamStatus(param) != Status.VALID ||\n            // Ask for optional parameters as well.\n            param.defaultValue === null) \n        {\n            var paramPrompt = param.description;\n            if (param.defaultValue === null) {\n                paramPrompt += \" (optional)\";\n            }\n            var value = prompt(paramPrompt, param.defaultValue || \"\");\n            // No value but required -> nope.\n            if (!value) {\n                callback();\n                return;\n            } else {\n                args[param.name] = value;\n            }           \n        }\n    }\n    callback();\n}\n\n/**\n * Entry point for keyboard accelerators or anything else that wants to execute\n * a command. A new request object is created and a check performed, if the\n * passed in arguments are VALID/INVALID or INCOMPLETE. If they are INCOMPLETE\n * the ArgumentProvider on the sender is called or otherwise the default \n * ArgumentProvider to get the still required arguments.\n * If they are valid (or valid after the ArgumentProvider is done), the command\n * is executed.\n * \n * @param command   Either a command, or the name of one\n * @param env       Current environment to execute the command in\n * @param sender    String that should be the same as the senderObject stored on \n *                  the environment in env[sender]\n * @param args      Arguments for the command\n * @param typed     (Optional)\n */\nfunction exec(command, env, sender, args, typed) {\n    if (typeof command === 'string') {\n        command = commands[command];\n    }\n    if (!command) {\n        // TODO: Should we complain more than returning false?\n        return false;\n    }\n\n    var request = new Request({\n        sender: sender,\n        command: command,\n        args: args || {},\n        typed: typed\n    });\n    \n    /**\n     * Executes the command and ensures request.done is called on the request in \n     * case it's not marked to be done already or async.\n     */\n    function execute() {\n        command.exec(env, request.args, request);\n        \n        // If the request isn't asnync and isn't done, then make it done.\n        if (!request.isAsync && !request.isDone) {\n            request.done();\n        }\n    }\n    \n    \n    if (request.getStatus() == Status.INVALID) {\n        console.error(\"Canon.exec: Invalid parameter(s) passed to \" + \n                            command.name);\n        return false;   \n    } \n    // If the request isn't complete yet, try to complete it.\n    else if (request.getStatus() == Status.INCOMPLETE) {\n        // Check if the sender has a ArgsProvider, otherwise use the default\n        // build in one.\n        var argsProvider;\n        var senderObj = env[sender];\n        if (!senderObj || !senderObj.getArgsProvider ||\n            !(argsProvider = senderObj.getArgsProvider())) \n        {\n            argsProvider = defaultArgsProvider;\n        }\n\n        // Ask the paramProvider to complete the request.\n        argsProvider(request, function() {\n            if (request.getStatus() == Status.VALID) {\n                execute();\n            }\n        });\n        return true;\n    } else {\n        execute();\n        return true;\n    }\n};\n\nexports.removeCommand = removeCommand;\nexports.addCommand = addCommand;\nexports.getCommand = getCommand;\nexports.getCommandNames = getCommandNames;\nexports.findKeyCommand = findKeyCommand;\nexports.exec = exec;\nexports.execKeyCommand = execKeyCommand;\nexports.upgradeType = upgradeType;\n\n\n/**\n * We publish a 'output' event whenever new command begins output\n * TODO: make this more obvious\n */\noop.implement(exports, EventEmitter);\n\n\n/**\n * Current requirements are around displaying the command line, and provision\n * of a 'history' command and cursor up|down navigation of history.\n * <p>Future requirements could include:\n * <ul>\n * <li>Multiple command lines\n * <li>The ability to recall key presses (i.e. requests with no output) which\n * will likely be needed for macro recording or similar\n * <li>The ability to store the command history either on the server or in the\n * browser local storage.\n * </ul>\n * <p>The execute() command doesn't really live here, except as part of that\n * last future requirement, and because it doesn't really have anywhere else to\n * live.\n */\n\n/**\n * The array of requests that wish to announce their presence\n */\nvar requests = [];\n\n/**\n * How many requests do we store?\n */\nvar maxRequestLength = 100;\n\n/**\n * To create an invocation, you need to do something like this (all the ctor\n * args are optional):\n * <pre>\n * var request = new Request({\n *     command: command,\n *     args: args,\n *     typed: typed\n * });\n * </pre>\n * @constructor\n */\nfunction Request(options) {\n    options = options || {};\n\n    // Will be used in the keyboard case and the cli case\n    this.command = options.command;\n\n    // Will be used only in the cli case\n    this.args = options.args;\n    this.typed = options.typed;\n\n    // Have we been initialized?\n    this._begunOutput = false;\n\n    this.start = new Date();\n    this.end = null;\n    this.completed = false;\n    this.error = false;\n};\n\noop.implement(Request.prototype, EventEmitter);\n\n/**\n * Return the status of a parameter on the request object.\n */\nRequest.prototype.getParamStatus = function(param) {\n    var args = this.args || {};\n    \n    // Check if there is already a value for this parameter.\n    if (param.name in args) {\n        // If there is no value set and then the value is VALID if it's not\n        // required or INCOMPLETE if not set yet.\n        if (args[param.name] == null) {\n            if (param.defaultValue === null) {\n                return Status.VALID;\n            } else {\n                return Status.INCOMPLETE;   \n            } \n        }\n        \n        // Check if the parameter value is valid.\n        var reply,\n            // The passed in value when parsing a type is a string.\n            argsValue = args[param.name].toString();\n        \n        // Type.parse can throw errors. \n        try {\n            reply = param.type.parse(argsValue);\n        } catch (e) {\n            return Status.INVALID;   \n        }\n        \n        if (reply.status != Status.VALID) {\n            return reply.status;   \n        }\n    } \n    // Check if the param is marked as required.\n    else if (param.defaultValue === undefined) {\n        // The parameter is not set on the args object but it's required,\n        // which means, things are invalid.\n        return Status.INCOMPLETE;\n    }\n    \n    return Status.VALID;\n}\n\n/**\n * Return the status of a parameter name on the request object.\n */\nRequest.prototype.getParamNameStatus = function(paramName) {\n    var params = this.command.params || [];\n    \n    for (var i = 0; i < params.length; i++) {\n        if (params[i].name == paramName) {\n            return this.getParamStatus(params[i]);   \n        }\n    }\n    \n    throw \"Parameter '\" + paramName + \n                \"' not defined on command '\" + this.command.name + \"'\"; \n}\n\n/**\n * Checks if all required arguments are set on the request such that it can\n * get executed.\n */\nRequest.prototype.getStatus = function() {\n    var args = this.args || {},\n        params = this.command.params;\n\n    // If there are not parameters, then it's valid.\n    if (!params || params.length == 0) {\n        return Status.VALID;\n    }\n\n    var status = [];\n    for (var i = 0; i < params.length; i++) {\n        status.push(this.getParamStatus(params[i]));        \n    }\n\n    return Status.combine(status);\n}\n\n/**\n * Lazy init to register with the history should only be done on output.\n * init() is expensive, and won't be used in the majority of cases\n */\nRequest.prototype._beginOutput = function() {\n    this._begunOutput = true;\n    this.outputs = [];\n\n    requests.push(this);\n    // This could probably be optimized with some maths, but 99.99% of the\n    // time we will only be off by one, and I'm feeling lazy.\n    while (requests.length > maxRequestLength) {\n        requests.shiftObject();\n    }\n\n    exports._dispatchEvent('output', { requests: requests, request: this });\n};\n\n/**\n * Sugar for:\n * <pre>request.error = true; request.done(output);</pre>\n */\nRequest.prototype.doneWithError = function(content) {\n    this.error = true;\n    this.done(content);\n};\n\n/**\n * Declares that this function will not be automatically done when\n * the command exits\n */\nRequest.prototype.async = function() {\n    this.isAsync = true;\n    if (!this._begunOutput) {\n        this._beginOutput();\n    }\n};\n\n/**\n * Complete the currently executing command with successful output.\n * @param output Either DOM node, an SproutCore element or something that\n * can be used in the content of a DIV to create a DOM node.\n */\nRequest.prototype.output = function(content) {\n    if (!this._begunOutput) {\n        this._beginOutput();\n    }\n\n    if (typeof content !== 'string' && !(content instanceof Node)) {\n        content = content.toString();\n    }\n\n    this.outputs.push(content);\n    this.isDone = true;\n    this._dispatchEvent('output', {});\n\n    return this;\n};\n\n/**\n * All commands that do output must call this to indicate that the command\n * has finished execution.\n */\nRequest.prototype.done = function(content) {\n    this.completed = true;\n    this.end = new Date();\n    this.duration = this.end.getTime() - this.start.getTime();\n\n    if (content) {\n        this.output(content);\n    }\n    \n    // Ensure to finish the request only once.\n    if (!this.isDone) {\n        this.isDone = true;\n        this._dispatchEvent('output', {});   \n    }\n};\nexports.Request = Request;\n\n\n});\n/* ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Mozilla Skywriter.\n *\n * The Initial Developer of the Original Code is\n * Mozilla.\n * Portions created by the Initial Developer are Copyright (C) 2009\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *   Joe Walker (jwalker@mozilla.com)\n *   Patrick Walton (pwalton@mozilla.com)\n *   Julian Viereck (jviereck@mozilla.com)\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\ndefine('pilot/console', function(require, exports, module) {\n    \n/**\n * This object represents a \"safe console\" object that forwards debugging\n * messages appropriately without creating a dependency on Firebug in Firefox.\n */\n\nvar noop = function() {};\n\n// These are the functions that are available in Chrome 4/5, Safari 4\n// and Firefox 3.6. Don't add to this list without checking browser support\nvar NAMES = [\n    \"assert\", \"count\", \"debug\", \"dir\", \"dirxml\", \"error\", \"group\", \"groupEnd\",\n    \"info\", \"log\", \"profile\", \"profileEnd\", \"time\", \"timeEnd\", \"trace\", \"warn\"\n];\n\nif (typeof(window) === 'undefined') {\n    // We're in a web worker. Forward to the main thread so the messages\n    // will show up.\n    NAMES.forEach(function(name) {\n        exports[name] = function() {\n            var args = Array.prototype.slice.call(arguments);\n            var msg = { op: 'log', method: name, args: args };\n            postMessage(JSON.stringify(msg));\n        };\n    });\n} else {\n    // For each of the console functions, copy them if they exist, stub if not\n    NAMES.forEach(function(name) {\n        if (window.console && window.console[name]) {\n            exports[name] = Function.prototype.bind.call(window.console[name], window.console);\n        } else {\n            exports[name] = noop;\n        }\n    });\n}\n\n});\ndefine('pilot/stacktrace', function(require, exports, module) {\n    \nvar ua = require(\"pilot/useragent\");\nvar console = require('pilot/console');\n\n// Changed to suit the specific needs of running within Skywriter\n\n// Domain Public by Eric Wendelin http://eriwen.com/ (2008)\n//                  Luke Smith http://lucassmith.name/ (2008)\n//                  Loic Dachary <loic@dachary.org> (2008)\n//                  Johan Euphrosine <proppy@aminche.com> (2008)\n//                  Øyvind Sean Kinsey http://kinsey.no/blog\n//\n// Information and discussions\n// http://jspoker.pokersource.info/skin/test-printstacktrace.html\n// http://eriwen.com/javascript/js-stack-trace/\n// http://eriwen.com/javascript/stacktrace-update/\n// http://pastie.org/253058\n// http://browsershots.org/http://jspoker.pokersource.info/skin/test-printstacktrace.html\n//\n\n//\n// guessFunctionNameFromLines comes from firebug\n//\n// Software License Agreement (BSD License)\n//\n// Copyright (c) 2007, Parakey Inc.\n// All rights reserved.\n//\n// Redistribution and use of this software in source and binary forms, with or without modification,\n// are permitted provided that the following conditions are met:\n//\n// * Redistributions of source code must retain the above\n//   copyright notice, this list of conditions and the\n//   following disclaimer.\n//\n// * Redistributions in binary form must reproduce the above\n//   copyright notice, this list of conditions and the\n//   following disclaimer in the documentation and/or other\n//   materials provided with the distribution.\n//\n// * Neither the name of Parakey Inc. nor the names of its\n//   contributors may be used to endorse or promote products\n//   derived from this software without specific prior\n//   written permission of Parakey Inc.\n//\n// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR\n// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND\n// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER\n// IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\n// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n\n\n/**\n * Different browsers create stack traces in different ways.\n * <strike>Feature</strike> Browser detection baby ;).\n */\nvar mode = (function() {\n\n    // We use SC's browser detection here to avoid the \"break on error\"\n    // functionality provided by Firebug. Firebug tries to do the right\n    // thing here and break, but it happens every time you load the page.\n    // bug 554105\n    if (ua.isGecko) {\n        return 'firefox';\n    } else if (ua.isOpera) {\n        return 'opera';\n    } else {\n        return 'other';\n    }\n\n    // SC doesn't do any detection of Chrome at this time.\n\n    // this is the original feature detection code that is used as a\n    // fallback.\n    try {\n        (0)();\n    } catch (e) {\n        if (e.arguments) {\n            return 'chrome';\n        }\n        if (e.stack) {\n            return 'firefox';\n        }\n        if (window.opera && !('stacktrace' in e)) { //Opera 9-\n            return 'opera';\n        }\n    }\n    return 'other';\n})();\n\n/**\n *\n */\nfunction stringifyArguments(args) {\n    for (var i = 0; i < args.length; ++i) {\n        var argument = args[i];\n        if (typeof argument == 'object') {\n            args[i] = '#object';\n        } else if (typeof argument == 'function') {\n            args[i] = '#function';\n        } else if (typeof argument == 'string') {\n            args[i] = '\"' + argument + '\"';\n        }\n    }\n    return args.join(',');\n}\n\n/**\n * Extract a stack trace from the format emitted by each browser.\n */\nvar decoders = {\n    chrome: function(e) {\n        var stack = e.stack;\n        if (!stack) {\n            console.log(e);\n            return [];\n        }\n        return stack.replace(/^.*?\\n/, '').\n                replace(/^.*?\\n/, '').\n                replace(/^.*?\\n/, '').\n                replace(/^[^\\(]+?[\\n$]/gm, '').\n                replace(/^\\s+at\\s+/gm, '').\n                replace(/^Object.<anonymous>\\s*\\(/gm, '{anonymous}()@').\n                split('\\n');\n    },\n\n    firefox: function(e) {\n        var stack = e.stack;\n        if (!stack) {\n            console.log(e);\n            return [];\n        }\n        // stack = stack.replace(/^.*?\\n/, '');\n        stack = stack.replace(/(?:\\n@:0)?\\s+$/m, '');\n        stack = stack.replace(/^\\(/gm, '{anonymous}(');\n        return stack.split('\\n');\n    },\n\n    // Opera 7.x and 8.x only!\n    opera: function(e) {\n        var lines = e.message.split('\\n'), ANON = '{anonymous}',\n            lineRE = /Line\\s+(\\d+).*?script\\s+(http\\S+)(?:.*?in\\s+function\\s+(\\S+))?/i, i, j, len;\n\n        for (i = 4, j = 0, len = lines.length; i < len; i += 2) {\n            if (lineRE.test(lines[i])) {\n                lines[j++] = (RegExp.$3 ? RegExp.$3 + '()@' + RegExp.$2 + RegExp.$1 : ANON + '()@' + RegExp.$2 + ':' + RegExp.$1) +\n                ' -- ' +\n                lines[i + 1].replace(/^\\s+/, '');\n            }\n        }\n\n        lines.splice(j, lines.length - j);\n        return lines;\n    },\n\n    // Safari, Opera 9+, IE, and others\n    other: function(curr) {\n        var ANON = '{anonymous}', fnRE = /function\\s*([\\w\\-$]+)?\\s*\\(/i, stack = [], j = 0, fn, args;\n\n        var maxStackSize = 10;\n        while (curr && stack.length < maxStackSize) {\n            fn = fnRE.test(curr.toString()) ? RegExp.$1 || ANON : ANON;\n            args = Array.prototype.slice.call(curr['arguments']);\n            stack[j++] = fn + '(' + stringifyArguments(args) + ')';\n\n            //Opera bug: if curr.caller does not exist, Opera returns curr (WTF)\n            if (curr === curr.caller && window.opera) {\n                //TODO: check for same arguments if possible\n                break;\n            }\n            curr = curr.caller;\n        }\n        return stack;\n    }\n};\n\n/**\n *\n */\nfunction NameGuesser() {\n}\n\nNameGuesser.prototype = {\n\n    sourceCache: {},\n\n    ajax: function(url) {\n        var req = this.createXMLHTTPObject();\n        if (!req) {\n            return;\n        }\n        req.open('GET', url, false);\n        req.setRequestHeader('User-Agent', 'XMLHTTP/1.0');\n        req.send('');\n        return req.responseText;\n    },\n\n    createXMLHTTPObject: function() {\n\t    // Try XHR methods in order and store XHR factory\n        var xmlhttp, XMLHttpFactories = [\n            function() {\n                return new XMLHttpRequest();\n            }, function() {\n                return new ActiveXObject('Msxml2.XMLHTTP');\n            }, function() {\n                return new ActiveXObject('Msxml3.XMLHTTP');\n            }, function() {\n                return new ActiveXObject('Microsoft.XMLHTTP');\n            }\n        ];\n        for (var i = 0; i < XMLHttpFactories.length; i++) {\n            try {\n                xmlhttp = XMLHttpFactories[i]();\n                // Use memoization to cache the factory\n                this.createXMLHTTPObject = XMLHttpFactories[i];\n                return xmlhttp;\n            } catch (e) {}\n        }\n    },\n\n    getSource: function(url) {\n        if (!(url in this.sourceCache)) {\n            this.sourceCache[url] = this.ajax(url).split('\\n');\n        }\n        return this.sourceCache[url];\n    },\n\n    guessFunctions: function(stack) {\n        for (var i = 0; i < stack.length; ++i) {\n            var reStack = /{anonymous}\\(.*\\)@(\\w+:\\/\\/([-\\w\\.]+)+(:\\d+)?[^:]+):(\\d+):?(\\d+)?/;\n            var frame = stack[i], m = reStack.exec(frame);\n            if (m) {\n                var file = m[1], lineno = m[4]; //m[7] is character position in Chrome\n                if (file && lineno) {\n                    var functionName = this.guessFunctionName(file, lineno);\n                    stack[i] = frame.replace('{anonymous}', functionName);\n                }\n            }\n        }\n        return stack;\n    },\n\n    guessFunctionName: function(url, lineNo) {\n        try {\n            return this.guessFunctionNameFromLines(lineNo, this.getSource(url));\n        } catch (e) {\n            return 'getSource failed with url: ' + url + ', exception: ' + e.toString();\n        }\n    },\n\n    guessFunctionNameFromLines: function(lineNo, source) {\n        var reFunctionArgNames = /function ([^(]*)\\(([^)]*)\\)/;\n        var reGuessFunction = /['\"]?([0-9A-Za-z_]+)['\"]?\\s*[:=]\\s*(function|eval|new Function)/;\n        // Walk backwards from the first line in the function until we find the line which\n        // matches the pattern above, which is the function definition\n        var line = '', maxLines = 10;\n        for (var i = 0; i < maxLines; ++i) {\n            line = source[lineNo - i] + line;\n            if (line !== undefined) {\n                var m = reGuessFunction.exec(line);\n                if (m) {\n                    return m[1];\n                }\n                else {\n                    m = reFunctionArgNames.exec(line);\n                }\n                if (m && m[1]) {\n                    return m[1];\n                }\n            }\n        }\n        return '(?)';\n    }\n};\n\nvar guesser = new NameGuesser();\n\nvar frameIgnorePatterns = [\n    /http:\\/\\/localhost:4020\\/sproutcore.js:/\n];\n\nexports.ignoreFramesMatching = function(regex) {\n    frameIgnorePatterns.push(regex);\n};\n\n/**\n * Create a stack trace from an exception\n * @param ex {Error} The error to create a stacktrace from (optional)\n * @param guess {Boolean} If we should try to resolve the names of anonymous functions\n */\nexports.Trace = function Trace(ex, guess) {\n    this._ex = ex;\n    this._stack = decoders[mode](ex);\n\n    if (guess) {\n        this._stack = guesser.guessFunctions(this._stack);\n    }\n};\n\n/**\n * Log to the console a number of lines (default all of them)\n * @param lines {number} Maximum number of lines to wrote to console\n */\nexports.Trace.prototype.log = function(lines) {\n    if (lines <= 0) {\n        // You aren't going to have more lines in your stack trace than this\n        // and it still fits in a 32bit integer\n        lines = 999999999;\n    }\n\n    var printed = 0;\n    for (var i = 0; i < this._stack.length && printed < lines; i++) {\n        var frame = this._stack[i];\n        var display = true;\n        frameIgnorePatterns.forEach(function(regex) {\n            if (regex.test(frame)) {\n                display = false;\n            }\n        });\n        if (display) {\n            console.debug(frame);\n            printed++;\n        }\n    }\n};\n\n});\n/* ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Ajax.org Code Editor (ACE).\n *\n * The Initial Developer of the Original Code is\n * Ajax.org B.V.\n * Portions created by the Initial Developer are Copyright (C) 2010\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *      Fabian Jakobs <fabian AT ajax DOT org>\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('pilot/useragent', function(require, exports, module) {\n\nvar os = (navigator.platform.match(/mac|win|linux/i) || [\"other\"])[0].toLowerCase();\nvar ua = navigator.userAgent;\nvar av = navigator.appVersion;\n\n/** Is the user using a browser that identifies itself as Windows */\nexports.isWin = (os == \"win\");\n\n/** Is the user using a browser that identifies itself as Mac OS */\nexports.isMac = (os == \"mac\");\n\n/** Is the user using a browser that identifies itself as Linux */\nexports.isLinux = (os == \"linux\");\n\nexports.isIE = ! + \"\\v1\";\n\n/** Is this Firefox or related? */\nexports.isGecko = exports.isMozilla = window.controllers && window.navigator.product === \"Gecko\";\n\n/** oldGecko == rev < 2.0 **/\nexports.isOldGecko = exports.isGecko && /rv\\:1/.test(navigator.userAgent);\n\n/** Is this Opera */\nexports.isOpera = window.opera && Object.prototype.toString.call(window.opera) == \"[object Opera]\";\n\n/** Is the user using a browser that identifies itself as WebKit */\nexports.isWebKit = parseFloat(ua.split(\"WebKit/\")[1]) || undefined;\n\nexports.isAIR = ua.indexOf(\"AdobeAIR\") >= 0;\n\nexports.isIPad = ua.indexOf(\"iPad\") >= 0;\n\n/**\n * I hate doing this, but we need some way to determine if the user is on a Mac\n * The reason is that users have different expectations of their key combinations.\n *\n * Take copy as an example, Mac people expect to use CMD or APPLE + C\n * Windows folks expect to use CTRL + C\n */\nexports.OS = {\n    LINUX: 'LINUX',\n    MAC: 'MAC',\n    WINDOWS: 'WINDOWS'\n};\n\n/**\n * Return an exports.OS constant\n */\nexports.getOS = function() {\n    if (exports.isMac) {\n        return exports.OS['MAC'];\n    } else if (exports.isLinux) {\n        return exports.OS['LINUX'];\n    } else {\n        return exports.OS['WINDOWS'];\n    }\n};\n\n});\n/* ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Ajax.org Code Editor (ACE).\n *\n * The Initial Developer of the Original Code is\n * Ajax.org B.V.\n * Portions created by the Initial Developer are Copyright (C) 2010\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *      Fabian Jakobs <fabian AT ajax DOT org>\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('pilot/oop', function(require, exports, module) {\n\nexports.inherits = (function() {\n    var tempCtor = function() {};\n    return function(ctor, superCtor) {\n        tempCtor.prototype = superCtor.prototype;\n        ctor.super_ = superCtor.prototype;\n        ctor.prototype = new tempCtor();\n        ctor.prototype.constructor = ctor;\n    }\n}());\n\nexports.mixin = function(obj, mixin) {\n    for (var key in mixin) {\n        obj[key] = mixin[key];\n    }\n};\n\nexports.implement = function(proto, mixin) {\n    exports.mixin(proto, mixin);\n};\n\n});\n/*! @license\n==========================================================================\nSproutCore -- JavaScript Application Framework\ncopyright 2006-2009, Sprout Systems Inc., Apple Inc. and contributors.\n\nPermission is hereby granted, free of charge, to any person obtaining a\ncopy of this software and associated documentation files (the \"Software\"),\nto deal in the Software without restriction, including without limitation\nthe rights to use, copy, modify, merge, publish, distribute, sublicense,\nand/or sell copies of the Software, and to permit persons to whom the\nSoftware is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n\nSproutCore and the SproutCore logo are trademarks of Sprout Systems, Inc.\n\nFor more information about SproutCore, visit http://www.sproutcore.com\n\n\n==========================================================================\n@license */\n\n// Most of the following code is taken from SproutCore with a few changes.\n\ndefine('pilot/keys', function(require, exports, module) {\n\nvar oop = require(\"pilot/oop\");\n\n/**\n * Helper functions and hashes for key handling.\n */\nvar Keys = (function() {\n    var ret = {\n        MODIFIER_KEYS: {\n            16: 'Shift', 17: 'Ctrl', 18: 'Alt', 224: 'Meta'\n        },\n\n        KEY_MODS: {\n            \"ctrl\": 1, \"alt\": 2, \"option\" : 2,\n            \"shift\": 4, \"meta\": 8, \"command\": 8\n        },\n\n        FUNCTION_KEYS : {\n            8  : \"Backspace\",\n            9  : \"Tab\",\n            13 : \"Return\",\n            19 : \"Pause\",\n            27 : \"Esc\",\n            32 : \"Space\",\n            33 : \"PageUp\",\n            34 : \"PageDown\",\n            35 : \"End\",\n            36 : \"Home\",\n            37 : \"Left\",\n            38 : \"Up\",\n            39 : \"Right\",\n            40 : \"Down\",\n            44 : \"Print\",\n            45 : \"Insert\",\n            46 : \"Delete\",\n            112: \"F1\",\n            113: \"F2\",\n            114: \"F3\",\n            115: \"F4\",\n            116: \"F5\",\n            117: \"F6\",\n            118: \"F7\",\n            119: \"F8\",\n            120: \"F9\",\n            121: \"F10\",\n            122: \"F11\",\n            123: \"F12\",\n            144: \"Numlock\",\n            145: \"Scrolllock\"\n        },\n\n        PRINTABLE_KEYS: {\n           32: ' ',  48: '0',  49: '1',  50: '2',  51: '3',  52: '4', 53:  '5',\n           54: '6',  55: '7',  56: '8',  57: '9',  59: ';',  61: '=', 65:  'a',\n           66: 'b',  67: 'c',  68: 'd',  69: 'e',  70: 'f',  71: 'g', 72:  'h',\n           73: 'i',  74: 'j',  75: 'k',  76: 'l',  77: 'm',  78: 'n', 79:  'o',\n           80: 'p',  81: 'q',  82: 'r',  83: 's',  84: 't',  85: 'u', 86:  'v',\n           87: 'w',  88: 'x',  89: 'y',  90: 'z', 107: '+', 109: '-', 110: '.',\n          188: ',', 190: '.', 191: '/', 192: '`', 219: '[', 220: '\\\\',\n          221: ']', 222: '\\\"'\n        }\n    };\n\n    // A reverse map of FUNCTION_KEYS\n    for (i in ret.FUNCTION_KEYS) {\n        var name = ret.FUNCTION_KEYS[i].toUpperCase();\n        ret[name] = parseInt(i, 10);\n    }\n\n    // Add the MODIFIER_KEYS, FUNCTION_KEYS and PRINTABLE_KEYS to the KEY\n    // variables as well.\n    oop.mixin(ret, ret.MODIFIER_KEYS);\n    oop.mixin(ret, ret.PRINTABLE_KEYS);\n    oop.mixin(ret, ret.FUNCTION_KEYS);\n\n    return ret;\n})();\noop.mixin(exports, Keys);\n\nexports.keyCodeToString = function(keyCode) {\n    return (Keys[keyCode] || String.fromCharCode(keyCode)).toLowerCase();\n}\n\n});\n/* vim:ts=4:sts=4:sw=4:\n * ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Ajax.org Code Editor (ACE).\n *\n * The Initial Developer of the Original Code is\n * Ajax.org B.V.\n * Portions created by the Initial Developer are Copyright (C) 2010\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *      Fabian Jakobs <fabian AT ajax DOT org>\n *      Irakli Gozalishvili <rfobic@gmail.com> (http://jeditoolkit.com)\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('pilot/event_emitter', function(require, exports, module) {\n\nvar EventEmitter = {};\n\nEventEmitter._emit =\nEventEmitter._dispatchEvent = function(eventName, e) {\n    this._eventRegistry = this._eventRegistry || {};\n\n    var listeners = this._eventRegistry[eventName];\n    if (!listeners || !listeners.length) return;\n\n    var e = e || {};\n    e.type = eventName;\n\n    for (var i=0; i<listeners.length; i++) {\n        listeners[i](e);\n    }\n};\n\nEventEmitter.on =\nEventEmitter.addEventListener = function(eventName, callback) {\n    this._eventRegistry = this._eventRegistry || {};\n\n    var listeners = this._eventRegistry[eventName];\n    if (!listeners) {\n      var listeners = this._eventRegistry[eventName] = [];\n    }\n    if (listeners.indexOf(callback) == -1) {\n        listeners.push(callback);\n    }\n};\n\nEventEmitter.removeListener =\nEventEmitter.removeEventListener = function(eventName, callback) {\n    this._eventRegistry = this._eventRegistry || {};\n\n    var listeners = this._eventRegistry[eventName];\n    if (!listeners) {\n      return;\n    }\n    var index = listeners.indexOf(callback);\n    if (index !== -1) {\n        listeners.splice(index, 1);\n    }\n};\n\nEventEmitter.removeAllListeners = function(eventName) {\n    if (this._eventRegistry) this._eventRegistry[eventName] = [];\n}\n\nexports.EventEmitter = EventEmitter;\n\n});\n/* ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Skywriter.\n *\n * The Initial Developer of the Original Code is\n * Mozilla.\n * Portions created by the Initial Developer are Copyright (C) 2009\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *   Joe Walker (jwalker@mozilla.com)\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('pilot/typecheck', function(require, exports, module) {\n\nvar objectToString = Object.prototype.toString;\n\n/**\n * Return true if it is a String\n */\nexports.isString = function(it) {\n    return it && objectToString.call(it) === \"[object String]\";\n};\n\n/**\n * Returns true if it is a Boolean.\n */\nexports.isBoolean = function(it) {\n    return it && objectToString.call(it) === \"[object Boolean]\";\n};\n\n/**\n * Returns true if it is a Number.\n */\nexports.isNumber = function(it) {\n    return it && objectToString.call(it) === \"[object Number]\" && isFinite(it);\n};\n\n/**\n * Hack copied from dojo.\n */\nexports.isObject = function(it) {\n    return it !== undefined &&\n        (it === null || typeof it == \"object\" ||\n        Array.isArray(it) || exports.isFunction(it));\n};\n\n/**\n * Is the passed object a function?\n * From dojo.isFunction()\n */\nexports.isFunction = function(it) {\n    return it && objectToString.call(it) === \"[object Function]\";\n};\n\n});/* ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Mozilla Skywriter.\n *\n * The Initial Developer of the Original Code is\n * Mozilla.\n * Portions created by the Initial Developer are Copyright (C) 2009\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *   Julian Viereck (jviereck@mozilla.com)\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('pilot/catalog', function(require, exports, module) {\n\n\nvar extensionSpecs = {};\n\nexports.addExtensionSpec = function(extensionSpec) {\n    extensionSpecs[extensionSpec.name] = extensionSpec;\n};\n\nexports.removeExtensionSpec = function(extensionSpec) {\n    if (typeof extensionSpec === \"string\") {\n        delete extensionSpecs[extensionSpec];\n    }\n    else {\n        delete extensionSpecs[extensionSpec.name];\n    }\n};\n\nexports.getExtensionSpec = function(name) {\n    return extensionSpecs[name];\n};\n\nexports.getExtensionSpecs = function() {\n    return Object.keys(extensionSpecs);\n};\n\n\n});\n/* ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Ajax.org Code Editor (ACE).\n *\n * The Initial Developer of the Original Code is\n * Ajax.org B.V.\n * Portions created by the Initial Developer are Copyright (C) 2010\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *      Fabian Jakobs <fabian AT ajax DOT org>\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('pilot/lang', function(require, exports, module) {\n\nexports.stringReverse = function(string) {\n    return string.split(\"\").reverse().join(\"\");\n};\n\nexports.stringRepeat = function (string, count) {\n     return new Array(count + 1).join(string);\n};\n\nexports.copyObject = function(obj) {\n    var copy = {};\n    for (var key in obj) {\n        copy[key] = obj[key];\n    }\n    return copy;\n};\n\nexports.arrayToMap = function(arr) {\n    var map = {};\n    for (var i=0; i<arr.length; i++) {\n        map[arr[i]] = 1;\n    }\n    return map;\n\n};\n\n/**\n * splice out of 'array' anything that === 'value'\n */\nexports.arrayRemove = function(array, value) {\n  for (var i = 0; i <= array.length; i++) {\n    if (value === array[i]) {\n      array.splice(i, 1);\n    }\n  }\n};\n\nexports.escapeRegExp = function(str) {\n    return str.replace(/([.*+?^${}()|[\\]\\/\\\\])/g, '\\\\$1');\n};\n\nexports.deferredCall = function(fcn) {\n\n    var timer = null;\n    var callback = function() {\n        timer = null;\n        fcn();\n    };\n\n    var deferred = function(timeout) {\n        if (!timer) {\n            timer = setTimeout(callback, timeout || 0);\n        }\n        return deferred;\n    }\n\n    deferred.schedule = deferred;\n    \n    deferred.call = function() {\n        this.cancel();\n        fcn();\n        return deferred;\n    };\n\n    deferred.cancel = function() {\n        clearTimeout(timer);\n        timer = null;\n        return deferred;\n    };\n    \n    return deferred;\n};\n\n});\n/* ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Mozilla Skywriter.\n *\n * The Initial Developer of the Original Code is\n * Mozilla.\n * Portions created by the Initial Developer are Copyright (C) 2009\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *      Joe Walker (jwalker@mozilla.com)\n *      Kevin Dangoor (kdangoor@mozilla.com)\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('pilot/types/settings', function(require, exports, module) {\n\nvar SelectionType = require('pilot/types/basic').SelectionType;\nvar DeferredType = require('pilot/types/basic').DeferredType;\nvar types = require('pilot/types');\nvar settings = require('pilot/settings').settings;\n\n\n/**\n * EVIL: This relies on us using settingValue in the same event as setting\n * The alternative is to have some central place where we store the current\n * command line, but this might be a lesser evil for now.\n */\nvar lastSetting;\n\n/**\n * Select from the available settings\n */\nvar setting = new SelectionType({\n    name: 'setting',\n    data: function() {\n        return env.settings.getSettingNames();\n    },\n    stringify: function(setting) {\n        lastSetting = setting;\n        return setting.name;\n    },\n    fromString: function(str) {\n        lastSetting = settings.getSetting(str);\n        return lastSetting;\n    },\n    noMatch: function() {\n        lastSetting = null;\n    }\n});\n\n/**\n * Something of a hack to allow the set command to give a clearer definition\n * of the type to the command line.\n */\nvar settingValue = new DeferredType({\n    name: 'settingValue',\n    defer: function() {\n        if (lastSetting) {\n            return lastSetting.type;\n        }\n        else {\n            return types.getType('text');\n        }\n    },\n    /**\n     * Promote the current value in any list of predictions, and add it if\n     * there are none.\n     */\n    getDefault: function() {\n        var conversion = this.parse('');\n        if (lastSetting) {\n            var current = lastSetting.get();\n            if (conversion.predictions.length === 0) {\n                conversion.predictions.push(current);\n            }\n            else {\n                // Remove current from predictions\n                var removed = false;\n                while (true) {\n                    var index = conversion.predictions.indexOf(current);\n                    if (index === -1) {\n                        break;\n                    }\n                    conversion.predictions.splice(index, 1);\n                    removed = true;\n                }\n                // If the current value wasn't something we would predict, leave it\n                if (removed) {\n                    conversion.predictions.push(current);\n                }\n            }\n        }\n        return conversion;\n    }\n});\n\nvar env;\n\n/**\n * Registration and de-registration.\n */\nexports.startup = function(data, reason) {\n    // TODO: this is probably all kinds of evil, but we need something working\n    env = data.env;\n    types.registerType(setting);\n    types.registerType(settingValue);\n};\n\nexports.shutdown = function(data, reason) {\n    types.unregisterType(setting);\n    types.unregisterType(settingValue);\n};\n\n\n});\n/* vim:ts=4:sts=4:sw=4:\n * ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Mozilla Skywriter.\n *\n * The Initial Developer of the Original Code is\n * Mozilla.\n * Portions created by the Initial Developer are Copyright (C) 2009\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *   Joe Walker (jwalker@mozilla.com)\n *   Julian Viereck (jviereck@mozilla.com)\n *   Kevin Dangoor (kdangoor@mozilla.com)\n *   Irakli Gozalishvili <rfobic@gmail.com> (http://jeditoolkit.com)\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('pilot/settings', function(require, exports, module) {\n\n/**\n * This plug-in manages settings.\n */\n\nvar console = require('pilot/console');\nvar oop = require('pilot/oop');\nvar types = require('pilot/types');\nvar EventEmitter = require('pilot/event_emitter').EventEmitter;\nvar catalog = require('pilot/catalog');\n\nvar settingExtensionSpec = {\n    name: 'setting',\n    description: 'A setting is something that the application offers as a ' +\n            'way to customize how it works',\n    register: 'env.settings.addSetting',\n    indexOn: 'name'\n};\n\nexports.startup = function(data, reason) {\n    catalog.addExtensionSpec(settingExtensionSpec);\n};\n\nexports.shutdown = function(data, reason) {\n    catalog.removeExtensionSpec(settingExtensionSpec);\n};\n\n\n/**\n * Create a new setting.\n * @param settingSpec An object literal that looks like this:\n * {\n *   name: 'thing',\n *   description: 'Thing is an example setting',\n *   type: 'string',\n *   defaultValue: 'something'\n * }\n */\nfunction Setting(settingSpec, settings) {\n    this._settings = settings;\n\n    Object.keys(settingSpec).forEach(function(key) {\n        this[key] = settingSpec[key];\n    }, this);\n\n    this.type = types.getType(this.type);\n    if (this.type == null) {\n        throw new Error('In ' + this.name +\n            ': can\\'t find type for: ' + JSON.stringify(settingSpec.type));\n    }\n\n    if (!this.name) {\n        throw new Error('Setting.name == undefined. Ignoring.', this);\n    }\n\n    if (!this.defaultValue === undefined) {\n        throw new Error('Setting.defaultValue == undefined', this);\n    }\n\n    if (this.onChange) {\n        this.on('change', this.onChange.bind(this))\n    }\n\n    this.set(this.defaultValue);\n}\nSetting.prototype = {\n    get: function() {\n        return this.value;\n    },\n\n    set: function(value) {\n        if (this.value === value) {\n            return;\n        }\n\n        this.value = value;\n        if (this._settings.persister) {\n            this._settings.persister.persistValue(this._settings, this.name, value);\n        }\n\n        this._dispatchEvent('change', { setting: this, value: value });\n    },\n\n    /**\n     * Reset the value of the <code>key</code> setting to it's default\n     */\n    resetValue: function() {\n        this.set(this.defaultValue);\n    }\n};\noop.implement(Setting.prototype, EventEmitter);\n\n\n/**\n * A base class for all the various methods of storing settings.\n * <p>Usage:\n * <pre>\n * // Create manually, or require 'settings' from the container.\n * // This is the manual version:\n * var settings = plugins.catalog.getObject('settings');\n * // Add a new setting\n * settings.addSetting({ name:'foo', ... });\n * // Display the default value\n * alert(settings.get('foo'));\n * // Alter the value, which also publishes the change etc.\n * settings.set('foo', 'bar');\n * // Reset the value to the default\n * settings.resetValue('foo');\n * </pre>\n * @constructor\n */\nfunction Settings(persister) {\n    // Storage for deactivated values\n    this._deactivated = {};\n\n    // Storage for the active settings\n    this._settings = {};\n    // We often want sorted setting names. Cache\n    this._settingNames = [];\n\n    if (persister) {\n        this.setPersister(persister);\n    }\n};\n\nSettings.prototype = {\n    /**\n     * Function to add to the list of available settings.\n     * <p>Example usage:\n     * <pre>\n     * var settings = plugins.catalog.getObject('settings');\n     * settings.addSetting({\n     *     name: 'tabsize', // For use in settings.get('X')\n     *     type: 'number',  // To allow value checking.\n     *     defaultValue: 4  // Default value for use when none is directly set\n     * });\n     * </pre>\n     * @param {object} settingSpec Object containing name/type/defaultValue members.\n     */\n    addSetting: function(settingSpec) {\n        var setting = new Setting(settingSpec, this);\n        this._settings[setting.name] = setting;\n        this._settingNames.push(setting.name);\n        this._settingNames.sort();\n    },\n\n    addSettings: function addSettings(settings) {\n        Object.keys(settings).forEach(function (name) {\n            var setting = settings[name];\n            if (!('name' in setting)) setting.name = name;\n            this.addSetting(setting);\n        }, this);\n    },\n\n    removeSetting: function(setting) {\n        var name = (typeof setting === 'string' ? setting : setting.name);\n        setting = this._settings[name];\n        delete this._settings[name];\n        util.arrayRemove(this._settingNames, name);\n        settings.removeAllListeners('change');\n    },\n\n    removeSettings: function removeSettings(settings) {\n        Object.keys(settings).forEach(function(name) {\n            var setting = settings[name];\n            if (!('name' in setting)) setting.name = name;\n            this.removeSettings(setting);\n        }, this);\n    },\n\n    getSettingNames: function() {\n        return this._settingNames;\n    },\n\n    getSetting: function(name) {\n        return this._settings[name];\n    },\n\n    /**\n     * A Persister is able to store settings. It is an object that defines\n     * two functions:\n     * loadInitialValues(settings) and persistValue(settings, key, value).\n     */\n    setPersister: function(persister) {\n        this._persister = persister;\n        if (persister) {\n            persister.loadInitialValues(this);\n        }\n    },\n\n    resetAll: function() {\n        this.getSettingNames().forEach(function(key) {\n            this.resetValue(key);\n        }, this);\n    },\n\n    /**\n     * Retrieve a list of the known settings and their values\n     */\n    _list: function() {\n        var reply = [];\n        this.getSettingNames().forEach(function(setting) {\n            reply.push({\n                'key': setting,\n                'value': this.getSetting(setting).get()\n            });\n        }, this);\n        return reply;\n    },\n\n    /**\n     * Prime the local cache with the defaults.\n     */\n    _loadDefaultValues: function() {\n        this._loadFromObject(this._getDefaultValues());\n    },\n\n    /**\n     * Utility to load settings from an object\n     */\n    _loadFromObject: function(data) {\n        // We iterate over data rather than keys so we don't forget values\n        // which don't have a setting yet.\n        for (var key in data) {\n            if (data.hasOwnProperty(key)) {\n                var setting = this._settings[key];\n                if (setting) {\n                    var value = setting.type.parse(data[key]);\n                    this.set(key, value);\n                } else {\n                    this.set(key, data[key]);\n                }\n            }\n        }\n    },\n\n    /**\n     * Utility to grab all the settings and export them into an object\n     */\n    _saveToObject: function() {\n        return this.getSettingNames().map(function(key) {\n            return this._settings[key].type.stringify(this.get(key));\n        }.bind(this));\n    },\n\n    /**\n     * The default initial settings\n     */\n    _getDefaultValues: function() {\n        return this.getSettingNames().map(function(key) {\n            return this._settings[key].spec.defaultValue;\n        }.bind(this));\n    }\n};\nexports.settings = new Settings();\n\n/**\n * Save the settings in a cookie\n * This code has not been tested since reboot\n * @constructor\n */\nfunction CookiePersister() {\n};\n\nCookiePersister.prototype = {\n    loadInitialValues: function(settings) {\n        settings._loadDefaultValues();\n        var data = cookie.get('settings');\n        settings._loadFromObject(JSON.parse(data));\n    },\n\n    persistValue: function(settings, key, value) {\n        try {\n            var stringData = JSON.stringify(settings._saveToObject());\n            cookie.set('settings', stringData);\n        } catch (ex) {\n            console.error('Unable to JSONify the settings! ' + ex);\n            return;\n        }\n    }\n};\n\nexports.CookiePersister = CookiePersister;\n\n});\n/* ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Skywriter.\n *\n * The Initial Developer of the Original Code is\n * Mozilla.\n * Portions created by the Initial Developer are Copyright (C) 2009\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *   Skywriter Team (skywriter@mozilla.com)\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('pilot/commands/settings', function(require, exports, module) {\n\n\nvar setCommandSpec = {\n    name: 'set',\n    params: [\n        {\n            name: 'setting',\n            type: 'setting',\n            description: 'The name of the setting to display or alter',\n            defaultValue: null\n        },\n        {\n            name: 'value',\n            type: 'settingValue',\n            description: 'The new value for the chosen setting',\n            defaultValue: null\n        }\n    ],\n    description: 'define and show settings',\n    exec: function(env, args, request) {\n        var html;\n        if (!args.setting) {\n            // 'set' by itself lists all the settings\n            var names = env.settings.getSettingNames();\n            html = '';\n            // first sort the settingsList based on the name\n            names.sort(function(name1, name2) {\n                return name1.localeCompare(name2);\n            });\n\n            names.forEach(function(name) {\n                var setting = env.settings.getSetting(name);\n                var url = 'https://wiki.mozilla.org/Labs/Skywriter/Settings#' +\n                        setting.name;\n                html += '<a class=\"setting\" href=\"' + url +\n                        '\" title=\"View external documentation on setting: ' +\n                        setting.name +\n                        '\" target=\"_blank\">' +\n                        setting.name +\n                        '</a> = ' +\n                        setting.value +\n                        '<br/>';\n            });\n        } else {\n            // set with only a setting, shows the value for that setting\n            if (args.value === undefined) {\n                html = '<strong>' + setting.name + '</strong> = ' +\n                        setting.get();\n            } else {\n                // Actually change the setting\n                args.setting.set(args.value);\n                html = 'Setting: <strong>' + args.setting.name + '</strong> = ' +\n                        args.setting.get();\n            }\n        }\n        request.done(html);\n    }\n};\n\nvar unsetCommandSpec = {\n    name: 'unset',\n    params: [\n        {\n            name: 'setting',\n            type: 'setting',\n            description: 'The name of the setting to return to defaults'\n        }\n    ],\n    description: 'unset a setting entirely',\n    exec: function(env, args, request) {\n        var setting = env.settings.get(args.setting);\n        if (!setting) {\n            request.doneWithError('No setting with the name <strong>' +\n                args.setting + '</strong>.');\n            return;\n        }\n\n        setting.reset();\n        request.done('Reset ' + setting.name + ' to default: ' +\n                env.settings.get(args.setting));\n    }\n};\n\nvar canon = require('pilot/canon');\n\nexports.startup = function(data, reason) {\n    canon.addCommand(setCommandSpec);\n    canon.addCommand(unsetCommandSpec);\n};\n\nexports.shutdown = function(data, reason) {\n    canon.removeCommand(setCommandSpec);\n    canon.removeCommand(unsetCommandSpec);\n};\n\n\n});\n/* ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Skywriter.\n *\n * The Initial Developer of the Original Code is\n * Mozilla.\n * Portions created by the Initial Developer are Copyright (C) 2009\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *   Skywriter Team (skywriter@mozilla.com)\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('pilot/commands/basic', function(require, exports, module) {\n\n\nvar checks = require(\"pilot/typecheck\");\nvar canon = require('pilot/canon');\n\n/**\n * \n */\nvar helpMessages = {\n    plainPrefix:\n        '<h2>Welcome to Skywriter - Code in the Cloud</h2><ul>' +\n        '<li><a href=\"http://labs.mozilla.com/projects/skywriter\" target=\"_blank\">Home Page</a></li>' +\n        '<li><a href=\"https://wiki.mozilla.org/Labs/Skywriter\" target=\"_blank\">Wiki</a></li>' +\n        '<li><a href=\"https://wiki.mozilla.org/Labs/Skywriter/UserGuide\" target=\"_blank\">User Guide</a></li>' +\n        '<li><a href=\"https://wiki.mozilla.org/Labs/Skywriter/Tips\" target=\"_blank\">Tips and Tricks</a></li>' +\n        '<li><a href=\"https://wiki.mozilla.org/Labs/Skywriter/FAQ\" target=\"_blank\">FAQ</a></li>' +\n        '<li><a href=\"https://wiki.mozilla.org/Labs/Skywriter/DeveloperGuide\" target=\"_blank\">Developers Guide</a></li>' +\n        '</ul>',\n    plainSuffix:\n        'For more information, see the <a href=\"https://wiki.mozilla.org/Labs/Skywriter\">Skywriter Wiki</a>.'\n};\n\n/**\n * 'help' command\n */\nvar helpCommandSpec = {\n    name: 'help',\n    params: [\n        {\n            name: 'search',\n            type: 'text',\n            description: 'Search string to narrow the output.',\n            defaultValue: null\n        }\n    ],\n    description: 'Get help on the available commands.',\n    exec: function(env, args, request) {\n        var output = [];\n\n        var command = canon.getCommand(args.search);\n        if (command && command.exec) {\n            // caught a real command\n            output.push(command.description ?\n                    command.description :\n                    'No description for ' + args.search);\n        } else {\n            var showHidden = false;\n\n            if (!args.search && helpMessages.plainPrefix) {\n                output.push(helpMessages.plainPrefix);\n            }\n\n            if (command) {\n                // We must be looking at sub-commands\n                output.push('<h2>Sub-Commands of ' + command.name + '</h2>');\n                output.push('<p>' + command.description + '</p>');\n            }\n            else if (args.search) {\n                if (args.search == 'hidden') { // sneaky, sneaky.\n                    args.search = '';\n                    showHidden = true;\n                }\n                output.push('<h2>Commands starting with \\'' + args.search + '\\':</h2>');\n            }\n            else {\n                output.push('<h2>Available Commands:</h2>');\n            }\n\n            var commandNames = canon.getCommandNames();\n            commandNames.sort();\n\n            output.push('<table>');\n            for (var i = 0; i < commandNames.length; i++) {\n                command = canon.getCommand(commandNames[i]);\n                if (!showHidden && command.hidden) {\n                    continue;\n                }\n                if (command.description === undefined) {\n                    // Ignore editor actions\n                    continue;\n                }\n                if (args.search && command.name.indexOf(args.search) !== 0) {\n                    // Filtered out by the user\n                    continue;\n                }\n                if (!args.search && command.name.indexOf(' ') != -1) {\n                    // sub command\n                    continue;\n                }\n                if (command && command.name == args.search) {\n                    // sub command, and we've already given that help\n                    continue;\n                }\n\n                // todo add back a column with parameter information, perhaps?\n\n                output.push('<tr>');\n                output.push('<th class=\"right\">' + command.name + '</th>');\n                output.push('<td>' + command.description + '</td>');\n                output.push('</tr>');\n            }\n            output.push('</table>');\n\n            if (!args.search && helpMessages.plainSuffix) {\n                output.push(helpMessages.plainSuffix);\n            }\n        }\n\n        request.done(output.join(''));\n    }\n};\n\n/**\n * 'eval' command\n */\nvar evalCommandSpec = {\n    name: 'eval',\n    params: [\n        {\n            name: 'javascript',\n            type: 'text',\n            description: 'The JavaScript to evaluate'\n        }\n    ],\n    description: 'evals given js code and show the result',\n    hidden: true,\n    exec: function(env, args, request) {\n        var result;\n        var javascript = args.javascript;\n        try {\n            result = eval(javascript);\n        } catch (e) {\n            result = '<b>Error: ' + e.message + '</b>';\n        }\n\n        var msg = '';\n        var type = '';\n        var x;\n\n        if (checks.isFunction(result)) {\n            // converts the function to a well formated string\n            msg = (result + '').replace(/\\n/g, '<br>').replace(/ /g, '&#160');\n            type = 'function';\n        } else if (checks.isObject(result)) {\n            if (Array.isArray(result)) {\n                type = 'array';\n            } else {\n                type = 'object';\n            }\n\n            var items = [];\n            var value;\n\n            for (x in result) {\n                if (result.hasOwnProperty(x)) {\n                    if (checks.isFunction(result[x])) {\n                        value = '[function]';\n                    } else if (checks.isObject(result[x])) {\n                        value = '[object]';\n                    } else {\n                        value = result[x];\n                    }\n\n                    items.push({name: x, value: value});\n                }\n            }\n\n            items.sort(function(a,b) {\n                return (a.name.toLowerCase() < b.name.toLowerCase()) ? -1 : 1;\n            });\n\n            for (x = 0; x < items.length; x++) {\n                msg += '<b>' + items[x].name + '</b>: ' + items[x].value + '<br>';\n            }\n\n        } else {\n            msg = result;\n            type = typeof result;\n        }\n\n        request.done('Result for eval <b>\\'' + javascript + '\\'</b>' +\n                ' (type: '+ type+'): <br><br>'+ msg);\n    }\n};\n\n/**\n * 'version' command\n */\nvar versionCommandSpec = {\n    name: 'version',\n    description: 'show the Skywriter version',\n    hidden: true,\n    exec: function(env, args, request) {\n        var version = 'Skywriter ' + skywriter.versionNumber + ' (' +\n                skywriter.versionCodename + ')';\n        request.done(version);\n    }\n};\n\n/**\n * 'skywriter' command\n */\nvar skywriterCommandSpec = {\n    name: 'skywriter',\n    hidden: true,\n    exec: function(env, args, request) {\n        var index = Math.floor(Math.random() * messages.length);\n        request.done('Skywriter ' + messages[index]);\n    }\n};\nvar messages = [\n    'really wants you to trick it out in some way.',\n    'is your Web editor.',\n    'would love to be like Emacs on the Web.',\n    'is written on the Web platform, so you can tweak it.'\n];\n\n\nvar canon = require('pilot/canon');\n\nexports.startup = function(data, reason) {\n    canon.addCommand(helpCommandSpec);\n    canon.addCommand(evalCommandSpec);\n    // canon.addCommand(versionCommandSpec);\n    canon.addCommand(skywriterCommandSpec);\n};\n\nexports.shutdown = function(data, reason) {\n    canon.removeCommand(helpCommandSpec);\n    canon.removeCommand(evalCommandSpec);\n    // canon.removeCommand(versionCommandSpec);\n    canon.removeCommand(skywriterCommandSpec);\n};\n\n\n});\n/* ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Mozilla Skywriter.\n *\n * The Initial Developer of the Original Code is\n * Mozilla.\n * Portions created by the Initial Developer are Copyright (C) 2009\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *   Joe Walker (jwalker@mozilla.com)\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('pilot/settings/canon', function(require, exports, module) {\n\n\nvar historyLengthSetting = {\n    name: \"historyLength\",\n    description: \"How many typed commands do we recall for reference?\",\n    type: \"number\",\n    defaultValue: 50\n};\n\nexports.startup = function(data, reason) {\n    data.env.settings.addSetting(historyLengthSetting);\n};\n\nexports.shutdown = function(data, reason) {\n    data.env.settings.removeSetting(historyLengthSetting);\n};\n\n\n});\n/* ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Skywriter.\n *\n * The Initial Developer of the Original Code is\n * Mozilla.\n * Portions created by the Initial Developer are Copyright (C) 2009\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *   Kevin Dangoor (kdangoor@mozilla.com)\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('pilot/plugin_manager', function(require, exports, module) {\n\nvar Promise = require(\"pilot/promise\").Promise;\n\nexports.REASONS = {\n    APP_STARTUP: 1,\n    APP_SHUTDOWN: 2,\n    PLUGIN_ENABLE: 3,\n    PLUGIN_DISABLE: 4,\n    PLUGIN_INSTALL: 5,\n    PLUGIN_UNINSTALL: 6,\n    PLUGIN_UPGRADE: 7,\n    PLUGIN_DOWNGRADE: 8\n};\n\nexports.Plugin = function(name) {\n    this.name = name;\n    this.status = this.INSTALLED;\n};\n\nexports.Plugin.prototype = {\n    /**\n     * constants for the state\n     */\n    NEW: 0,\n    INSTALLED: 1,\n    REGISTERED: 2,\n    STARTED: 3,\n    UNREGISTERED: 4,\n    SHUTDOWN: 5,\n\n    install: function(data, reason) {\n        var pr = new Promise();\n        if (this.status > this.NEW) {\n            pr.resolve(this);\n            return pr;\n        }\n        require([this.name], function(pluginModule) {\n            if (pluginModule.install) {\n                pluginModule.install(data, reason);\n            }\n            this.status = this.INSTALLED;\n            pr.resolve(this);\n        }.bind(this));\n        return pr;\n    },\n\n    register: function(data, reason) {\n        var pr = new Promise();\n        if (this.status != this.INSTALLED) {\n            pr.resolve(this);\n            return pr;\n        }\n        require([this.name], function(pluginModule) {\n            if (pluginModule.register) {\n                pluginModule.register(data, reason);\n            }\n            this.status = this.REGISTERED;\n            pr.resolve(this);\n        }.bind(this));\n        return pr;\n    },\n\n    startup: function(data, reason) {\n        reason = reason || exports.REASONS.APP_STARTUP;\n        var pr = new Promise();\n        if (this.status != this.REGISTERED) {\n            pr.resolve(this);\n            return pr;\n        }\n        require([this.name], function(pluginModule) {\n            if (pluginModule.startup) {\n                pluginModule.startup(data, reason);\n            }\n            this.status = this.STARTED;\n            pr.resolve(this);\n        }.bind(this));\n        return pr;\n    },\n\n    shutdown: function(data, reason) {\n        if (this.status != this.STARTED) {\n            return;\n        }\n        pluginModule = require(this.name);\n        if (pluginModule.shutdown) {\n            pluginModule.shutdown(data, reason);\n        }\n    }\n};\n\nexports.PluginCatalog = function() {\n    this.plugins = {};\n};\n\nexports.PluginCatalog.prototype = {\n    registerPlugins: function(pluginList, data, reason) {\n        var registrationPromises = [];\n        pluginList.forEach(function(pluginName) {\n            var plugin = this.plugins[pluginName];\n            if (plugin === undefined) {\n                plugin = new exports.Plugin(pluginName);\n                this.plugins[pluginName] = plugin;\n                registrationPromises.push(plugin.register(data, reason));\n            }\n        }.bind(this));\n        return Promise.group(registrationPromises);\n    },\n\n    startupPlugins: function(data, reason) {\n        var startupPromises = [];\n        for (var pluginName in this.plugins) {\n            var plugin = this.plugins[pluginName];\n            startupPromises.push(plugin.startup(data, reason));\n        }\n        return Promise.group(startupPromises);\n    }\n};\n\nexports.catalog = new exports.PluginCatalog();\n\n});\n/* ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Mozilla Skywriter.\n *\n * The Initial Developer of the Original Code is\n * Mozilla.\n * Portions created by the Initial Developer are Copyright (C) 2009\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *   Joe Walker (jwalker@mozilla.com)\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('pilot/promise', function(require, exports, module) {\n\nvar console = require(\"pilot/console\");\nvar Trace = require('pilot/stacktrace').Trace;\n\n/**\n * A promise can be in one of 2 states.\n * The ERROR and SUCCESS states are terminal, the PENDING state is the only\n * start state.\n */\nvar ERROR = -1;\nvar PENDING = 0;\nvar SUCCESS = 1;\n\n/**\n * We give promises and ID so we can track which are outstanding\n */\nvar _nextId = 0;\n\n/**\n * Debugging help if 2 things try to complete the same promise.\n * This can be slow (especially on chrome due to the stack trace unwinding) so\n * we should leave this turned off in normal use.\n */\nvar _traceCompletion = false;\n\n/**\n * Outstanding promises. Handy list for debugging only.\n */\nvar _outstanding = [];\n\n/**\n * Recently resolved promises. Also for debugging only.\n */\nvar _recent = [];\n\n/**\n * Create an unfulfilled promise\n */\nPromise = function () {\n    this._status = PENDING;\n    this._value = undefined;\n    this._onSuccessHandlers = [];\n    this._onErrorHandlers = [];\n\n    // Debugging help\n    this._id = _nextId++;\n    //this._createTrace = new Trace(new Error());\n    _outstanding[this._id] = this;\n};\n\n/**\n * Yeay for RTTI.\n */\nPromise.prototype.isPromise = true;\n\n/**\n * Have we either been resolve()ed or reject()ed?\n */\nPromise.prototype.isComplete = function() {\n    return this._status != PENDING;\n};\n\n/**\n * Have we resolve()ed?\n */\nPromise.prototype.isResolved = function() {\n    return this._status == SUCCESS;\n};\n\n/**\n * Have we reject()ed?\n */\nPromise.prototype.isRejected = function() {\n    return this._status == ERROR;\n};\n\n/**\n * Take the specified action of fulfillment of a promise, and (optionally)\n * a different action on promise rejection.\n */\nPromise.prototype.then = function(onSuccess, onError) {\n    if (typeof onSuccess === 'function') {\n        if (this._status === SUCCESS) {\n            onSuccess.call(null, this._value);\n        } else if (this._status === PENDING) {\n            this._onSuccessHandlers.push(onSuccess);\n        }\n    }\n\n    if (typeof onError === 'function') {\n        if (this._status === ERROR) {\n            onError.call(null, this._value);\n        } else if (this._status === PENDING) {\n            this._onErrorHandlers.push(onError);\n        }\n    }\n\n    return this;\n};\n\n/**\n * Like then() except that rather than returning <tt>this</tt> we return\n * a promise which\n */\nPromise.prototype.chainPromise = function(onSuccess) {\n    var chain = new Promise();\n    chain._chainedFrom = this;\n    this.then(function(data) {\n        try {\n            chain.resolve(onSuccess(data));\n        } catch (ex) {\n            chain.reject(ex);\n        }\n    }, function(ex) {\n        chain.reject(ex);\n    });\n    return chain;\n};\n\n/**\n * Supply the fulfillment of a promise\n */\nPromise.prototype.resolve = function(data) {\n    return this._complete(this._onSuccessHandlers, SUCCESS, data, 'resolve');\n};\n\n/**\n * Renege on a promise\n */\nPromise.prototype.reject = function(data) {\n    return this._complete(this._onErrorHandlers, ERROR, data, 'reject');\n};\n\n/**\n * Internal method to be called on resolve() or reject().\n * @private\n */\nPromise.prototype._complete = function(list, status, data, name) {\n    // Complain if we've already been completed\n    if (this._status != PENDING) {\n        console.group('Promise already closed');\n        console.error('Attempted ' + name + '() with ', data);\n        console.error('Previous status = ', this._status,\n                ', previous value = ', this._value);\n        console.trace();\n\n        if (this._completeTrace) {\n            console.error('Trace of previous completion:');\n            this._completeTrace.log(5);\n        }\n        console.groupEnd();\n        return this;\n    }\n\n    if (_traceCompletion) {\n        this._completeTrace = new Trace(new Error());\n    }\n\n    this._status = status;\n    this._value = data;\n\n    // Call all the handlers, and then delete them\n    list.forEach(function(handler) {\n        handler.call(null, this._value);\n    }, this);\n    this._onSuccessHandlers.length = 0;\n    this._onErrorHandlers.length = 0;\n\n    // Remove the given {promise} from the _outstanding list, and add it to the\n    // _recent list, pruning more than 20 recent promises from that list.\n    delete _outstanding[this._id];\n    _recent.push(this);\n    while (_recent.length > 20) {\n        _recent.shift();\n    }\n\n    return this;\n};\n\n/**\n * Takes an array of promises and returns a promise that that is fulfilled once\n * all the promises in the array are fulfilled\n * @param group The array of promises\n * @return the promise that is fulfilled when all the array is fulfilled\n */\nPromise.group = function(promiseList) {\n    if (!(promiseList instanceof Array)) {\n        promiseList = Array.prototype.slice.call(arguments);\n    }\n\n    // If the original array has nothing in it, return now to avoid waiting\n    if (promiseList.length === 0) {\n        return new Promise().resolve([]);\n    }\n\n    var groupPromise = new Promise();\n    var results = [];\n    var fulfilled = 0;\n\n    var onSuccessFactory = function(index) {\n        return function(data) {\n            results[index] = data;\n            fulfilled++;\n            // If the group has already failed, silently drop extra results\n            if (groupPromise._status !== ERROR) {\n                if (fulfilled === promiseList.length) {\n                    groupPromise.resolve(results);\n                }\n            }\n        };\n    };\n\n    promiseList.forEach(function(promise, index) {\n        var onSuccess = onSuccessFactory(index);\n        var onError = groupPromise.reject.bind(groupPromise);\n        promise.then(onSuccess, onError);\n    });\n\n    return groupPromise;\n};\n\nexports.Promise = Promise;\nexports._outstanding = _outstanding;\nexports._recent = _recent;\n\n});\n/* ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is DomTemplate.\n *\n * The Initial Developer of the Original Code is Mozilla.\n * Portions created by the Initial Developer are Copyright (C) 2009\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *   Joe Walker (jwalker@mozilla.com) (original author)\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('pilot/environment', function(require, exports, module) {\n\n\nvar settings = require(\"pilot/settings\").settings;\n\n/**\n * Create an environment object\n */\nfunction create() {\n    return {\n        settings: settings\n    };\n};\n\nexports.create = create;\n\n\n});\n/* vim:ts=4:sts=4:sw=4:\n * ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Ajax.org Code Editor (ACE).\n *\n * The Initial Developer of the Original Code is\n * Ajax.org B.V.\n * Portions created by the Initial Developer are Copyright (C) 2010\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *      Fabian Jakobs <fabian AT ajax DOT org>\n *      Irakli Gozalishvili <rfobic@gmail.com> (http://jeditoolkit.com)\n *      Julian Viereck <julian.viereck@gmail.com>\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('ace/editor', function(require, exports, module) {\n\nrequire(\"pilot/fixoldbrowsers\");\n\nvar oop = require(\"pilot/oop\");\nvar event = require(\"pilot/event\");\nvar lang = require(\"pilot/lang\");\nvar useragent = require(\"pilot/useragent\");\nvar TextInput = require(\"ace/keyboard/textinput\").TextInput;\nvar MouseHandler = require(\"ace/mouse_handler\").MouseHandler;\n//var TouchHandler = require(\"ace/touch_handler\").TouchHandler;\nvar KeyBinding = require(\"ace/keyboard/keybinding\").KeyBinding;\nvar EditSession = require(\"ace/edit_session\").EditSession;\nvar Search = require(\"ace/search\").Search;\nvar BackgroundTokenizer = require(\"ace/background_tokenizer\").BackgroundTokenizer;\nvar Range = require(\"ace/range\").Range;\nvar EventEmitter = require(\"pilot/event_emitter\").EventEmitter;\n\nvar Editor =function(renderer, session) {\n    var container = renderer.getContainerElement();\n    this.container = container;\n    this.renderer = renderer;\n\n    this.textInput  = new TextInput(renderer.getTextAreaContainer(), this);\n    this.keyBinding = new KeyBinding(this);\n    \n    // TODO detect touch event support\n    if (useragent.isIPad) {\n        //this.$mouseHandler = new TouchHandler(this);\n    } else {\n        this.$mouseHandler = new MouseHandler(this);\n    }\n\n    this.$blockScrolling = 0;\n    this.$search = new Search().set({\n        wrap: true\n    });\n\n    this.setSession(session || new EditSession(\"\"));\n};\n\n(function(){\n\n    oop.implement(this, EventEmitter);\n\n    this.$forwardEvents = {\n        gutterclick: 1,\n        gutterdblclick: 1\n    };\n\n    this.$originalAddEventListener = this.addEventListener;\n    this.$originalRemoveEventListener = this.removeEventListener;\n\n    this.addEventListener = function(eventName, callback) {\n        if (this.$forwardEvents[eventName]) {\n            return this.renderer.addEventListener(eventName, callback);\n        } else {\n            return this.$originalAddEventListener(eventName, callback);\n        }\n    };\n\n    this.removeEventListener = function(eventName, callback) {\n        if (this.$forwardEvents[eventName]) {\n            return this.renderer.removeEventListener(eventName, callback);\n        } else {\n            return this.$originalRemoveEventListener(eventName, callback);\n        }\n    };\n\n    this.setKeyboardHandler = function(keyboardHandler) {\n        this.keyBinding.setKeyboardHandler(keyboardHandler);\n    };\n\n    this.getKeyboardHandler = function() {\n        return this.keyBinding.getKeyboardHandler();\n    };\n\n    this.setSession = function(session) {\n        if (this.session == session) return;\n\n        if (this.session) {\n            var oldSession = this.session;\n            this.session.removeEventListener(\"change\", this.$onDocumentChange);\n            this.session.removeEventListener(\"changeMode\", this.$onChangeMode);\n            this.session.removeEventListener(\"changeTabSize\", this.$onChangeTabSize);\n            this.session.removeEventListener(\"changeWrapLimit\", this.$onChangeWrapLimit);\n            this.session.removeEventListener(\"changeWrapMode\", this.$onChangeWrapMode);\n            this.session.removeEventListener(\"changeFrontMarker\", this.$onChangeFrontMarker);\n            this.session.removeEventListener(\"changeBackMarker\", this.$onChangeBackMarker);\n            this.session.removeEventListener(\"changeBreakpoint\", this.$onChangeBreakpoint);\n            this.session.removeEventListener(\"changeAnnotation\", this.$onChangeAnnotation);\n            this.session.removeEventListener(\"changeOverwrite\", this.$onCursorChange);\n\n            var selection = this.session.getSelection();\n            selection.removeEventListener(\"changeCursor\", this.$onCursorChange);\n            selection.removeEventListener(\"changeSelection\", this.$onSelectionChange);\n\n            this.session.setScrollTopRow(this.renderer.getScrollTopRow());\n        }\n\n        this.session = session;\n\n        this.$onDocumentChange = this.onDocumentChange.bind(this);\n        session.addEventListener(\"change\", this.$onDocumentChange);\n        this.renderer.setSession(session);\n\n        this.$onChangeMode = this.onChangeMode.bind(this);\n        session.addEventListener(\"changeMode\", this.$onChangeMode);\n\n        this.$onChangeTabSize = this.renderer.updateText.bind(this.renderer);\n        session.addEventListener(\"changeTabSize\", this.$onChangeTabSize);\n\n        this.$onChangeWrapLimit = this.onChangeWrapLimit.bind(this);\n        session.addEventListener(\"changeWrapLimit\", this.$onChangeWrapLimit);\n\n        this.$onChangeWrapMode = this.onChangeWrapMode.bind(this);\n        session.addEventListener(\"changeWrapMode\", this.$onChangeWrapMode);\n\n        this.$onChangeFrontMarker = this.onChangeFrontMarker.bind(this);\n        this.session.addEventListener(\"changeFrontMarker\", this.$onChangeFrontMarker);\n        \n        this.$onChangeBackMarker = this.onChangeBackMarker.bind(this);\n        this.session.addEventListener(\"changeBackMarker\", this.$onChangeBackMarker);\n        \n        this.$onChangeBreakpoint = this.onChangeBreakpoint.bind(this);\n        this.session.addEventListener(\"changeBreakpoint\", this.$onChangeBreakpoint);\n\n        this.$onChangeAnnotation = this.onChangeAnnotation.bind(this);\n        this.session.addEventListener(\"changeAnnotation\", this.$onChangeAnnotation);\n        \n        this.$onCursorChange = this.onCursorChange.bind(this);\n        this.session.addEventListener(\"changeOverwrite\", this.$onCursorChange);\n\n        this.selection = session.getSelection();\n        this.selection.addEventListener(\"changeCursor\", this.$onCursorChange);\n\n        this.$onSelectionChange = this.onSelectionChange.bind(this);\n        this.selection.addEventListener(\"changeSelection\", this.$onSelectionChange);\n\n        this.onChangeMode();\n        this.bgTokenizer.setDocument(session.getDocument());\n        this.bgTokenizer.start(0);\n\n        this.onCursorChange();\n        this.onSelectionChange();\n        this.onChangeFrontMarker();\n        this.onChangeBackMarker();\n        this.onChangeBreakpoint();\n        this.onChangeAnnotation();\n        this.renderer.scrollToRow(session.getScrollTopRow());\n        this.renderer.updateFull();\n\n        this._dispatchEvent(\"changeSession\", {\n            session: session,\n            oldSession: oldSession\n        });\n    };\n\n    this.getSession = function() {\n        return this.session;\n    };\n\n    this.getSelection = function() {\n        return this.selection;\n    };\n\n    this.resize = function() {\n        this.renderer.onResize();\n    };\n\n    this.setTheme = function(theme) {\n        this.renderer.setTheme(theme);\n    };\n\n    this.setStyle = function(style) {\n        this.renderer.setStyle(style)\n    };\n\n    this.unsetStyle = function(style) {\n        this.renderer.unsetStyle(style)\n    }\n\n    this.$highlightBrackets = function() {\n        if (this.session.$bracketHighlight) {\n            this.session.removeMarker(this.session.$bracketHighlight);\n            this.session.$bracketHighlight = null;\n        }\n\n        if (this.$highlightPending) {\n            return;\n        }\n\n        // perform highlight async to not block the browser during navigation\n        var self = this;\n        this.$highlightPending = true;\n        setTimeout(function() {\n            self.$highlightPending = false;\n\n            var pos = self.session.findMatchingBracket(self.getCursorPosition());\n            if (pos) {\n                var range = new Range(pos.row, pos.column, pos.row, pos.column+1);\n                self.session.$bracketHighlight = self.session.addMarker(range, \"ace_bracket\");\n            }\n        }, 10);\n    };\n\n    this.focus = function() {\n        // Safari needs the timeout\n        // iOS and Firefox need it called immediately\n        // to be on the save side we do both\n        // except for IE\n        var _self = this;\n        if (!useragent.isIE) {        \n            setTimeout(function() {\n                _self.textInput.focus();\n            });\n        }\n        this.textInput.focus();\n    };\n\n    this.blur = function() {\n        this.textInput.blur();\n    };\n\n    this.onFocus = function() {\n        this.renderer.showCursor();\n        this.renderer.visualizeFocus();\n        this._dispatchEvent(\"focus\");\n    };\n\n    this.onBlur = function() {\n        this.renderer.hideCursor();\n        this.renderer.visualizeBlur();\n        this._dispatchEvent(\"blur\");\n    };\n\n    this.onDocumentChange = function(e) {\n        var delta = e.data;\n        var range = delta.range;\n\n        this.bgTokenizer.start(range.start.row);\n        if (range.start.row == range.end.row && delta.action != \"insertLines\" && delta.action != \"removeLines\")\n            var lastRow = range.end.row;\n        else\n            lastRow = Infinity;\n        this.renderer.updateLines(range.start.row, lastRow);\n\n        // update cursor because tab characters can influence the cursor position\n        this.renderer.updateCursor();\n    };\n\n    this.onTokenizerUpdate = function(e) {\n        var rows = e.data;\n        this.renderer.updateLines(rows.first, rows.last);\n    };\n\n    this.onCursorChange = function(e) {\n        this.renderer.updateCursor();\n\n        if (!this.$blockScrolling) {\n            this.renderer.scrollCursorIntoView();\n        }\n\n        // move text input over the cursor\n        // this is required for iOS and IME\n        this.renderer.moveTextAreaToCursor(this.textInput.getElement());\n\n        this.$highlightBrackets();\n        this.$updateHighlightActiveLine();\n    };\n\n    this.$updateHighlightActiveLine = function() {\n        var session = this.getSession();\n        \n        if (session.$highlightLineMarker) {\n            session.removeMarker(session.$highlightLineMarker);\n        }\n        session.$highlightLineMarker = null;\n\n        if (this.getHighlightActiveLine() && (this.getSelectionStyle() != \"line\" || !this.selection.isMultiLine())) {\n            var cursor = this.getCursorPosition();\n            var range = new Range(cursor.row, 0, cursor.row+1, 0);\n            session.$highlightLineMarker = session.addMarker(range, \"ace_active_line\", \"line\");\n        }\n    };\n\n    this.onSelectionChange = function(e) {\n        var session = this.getSession();\n        \n        if (session.$selectionMarker) {\n            session.removeMarker(session.$selectionMarker);\n        }\n        session.$selectionMarker = null;\n\n        if (!this.selection.isEmpty()) {\n            var range = this.selection.getRange();\n            var style = this.getSelectionStyle();\n            session.$selectionMarker = session.addMarker(range, \"ace_selection\", style);\n        }\n\n        this.onCursorChange(e);\n\n        if (this.$highlightSelectedWord)\n            this.mode.highlightSelection(this);\n    };\n\n    this.onChangeFrontMarker = function() {\n        this.renderer.updateFrontMarkers();\n    };\n    \n    this.onChangeBackMarker = function() {\n        this.renderer.updateBackMarkers();\n    };\n    \n    this.onChangeBreakpoint = function() {\n        this.renderer.setBreakpoints(this.session.getBreakpoints());\n    };\n\n    this.onChangeAnnotation = function() {\n        this.renderer.setAnnotations(this.session.getAnnotations());\n    };\n\n    this.onChangeMode = function() {\n        var mode = this.session.getMode();\n        if (this.mode == mode)\n            return;\n\n        this.mode = mode;\n        var tokenizer = mode.getTokenizer();\n\n        if (!this.bgTokenizer) {\n            var onUpdate = this.onTokenizerUpdate.bind(this);\n            this.bgTokenizer = new BackgroundTokenizer(tokenizer, this);\n            this.bgTokenizer.addEventListener(\"update\", onUpdate);\n        } else {\n            this.bgTokenizer.setTokenizer(tokenizer);\n        }\n\n        this.renderer.setTokenizer(this.bgTokenizer);\n    };\n\n    this.onChangeWrapLimit = function() {\n        this.renderer.updateFull();\n    };\n\n    this.onChangeWrapMode = function() {\n        this.renderer.onResize(true);\n    };\n\n    this.getCopyText = function() {\n        if (!this.selection.isEmpty()) {\n            return this.session.getTextRange(this.getSelectionRange());\n        }\n        else {\n            return \"\";\n        }\n    };\n\n    this.onCut = function() {\n        if (this.$readOnly)\n            return;\n\n        if (!this.selection.isEmpty()) {\n            this.session.remove(this.getSelectionRange())\n            this.clearSelection();\n        }\n    };\n\n    this.insert = function(text) {\n        if (this.$readOnly)\n            return;\n\n        var cursor = this.getCursorPosition();\n        text = text.replace(\"\\t\", this.session.getTabString());\n\n        // remove selected text\n        if (!this.selection.isEmpty()) {\n            var cursor = this.session.remove(this.getSelectionRange());\n            this.clearSelection();\n        }\n        else if (this.session.getOverwrite()) {\n            var range = new Range.fromPoints(cursor, cursor);\n            range.end.column += text.length;\n            this.session.remove(range);\n        }\n\n        this.clearSelection();\n\n        var lineState     = this.bgTokenizer.getState(cursor.row);\n        var shouldOutdent = this.mode.checkOutdent(lineState, this.session.getLine(cursor.row), text);\n        var line          = this.session.getLine(cursor.row);\n        var lineIndent    = this.mode.getNextLineIndent(lineState, line.slice(0, cursor.column), this.session.getTabString());\n        var end           = this.session.insert(cursor, text);\n\n        \n        var lineState = this.bgTokenizer.getState(cursor.row);\n        // TODO disabled multiline auto indent\n        // possibly doing the indent before inserting the text\n        // if (cursor.row !== end.row) {\n        if (this.session.getDocument().isNewLine(text)) {\n            this.moveCursorTo(cursor.row+1, 0);\n            \n            var size        = this.session.getTabSize(),\n                minIndent   = Number.MAX_VALUE;\n\n            \n            for (var row = cursor.row + 1; row <= end.row; ++row) {\n                var indent = 0;\n\n                line = this.session.getLine(row);\n                for (var i = 0; i < line.length; ++i)\n                    if (line.charAt(i) == '\\t')\n                        indent += size;\n                    else if (line.charAt(i) == ' ')\n                        indent += 1;\n                    else\n                        break;\n                if (/[^\\s]/.test(line))\n                    minIndent = Math.min(indent, minIndent);\n            }\n\n            for (var row = cursor.row + 1; row <= end.row; ++row) {\n                var outdent = minIndent;\n\n                line = this.session.getLine(row);\n                for (var i = 0; i < line.length && outdent > 0; ++i)\n                    if (line.charAt(i) == '\\t')\n                        outdent -= size;\n                    else if (line.charAt(i) == ' ')\n                        outdent -= 1;\n                this.session.remove(new Range(row, 0, row, i));\n            }\n            this.session.indentRows(cursor.row + 1, end.row, lineIndent);\n        } else {\n            if (shouldOutdent) {\n                this.mode.autoOutdent(lineState, this.session, cursor.row);\n            }\n        };        \n    }\n\n    this.onTextInput = function(text) {\n        this.keyBinding.onTextInput(text);\n    };\n\n    this.onCommandKey = function(e, hashId, keyCode) {\n        this.keyBinding.onCommandKey(e, hashId, keyCode);\n    };\n\n    this.setOverwrite = function(overwrite) {\n        this.session.setOverwrite();        \n    };\n\n    this.getOverwrite = function() {\n        return this.session.getOverwrite();\n    };\n\n    this.toggleOverwrite = function() {\n        this.session.toggleOverwrite();\n    };\n\n    this.setScrollSpeed = function(speed) {\n        this.$mouseHandler.setScrollSpeed(speed);\n    };\n\n    this.getScrollSpeed = function() {\n        return this.$mouseHandler.getScrollSpeed()\n    };\n\n    this.$selectionStyle = \"line\";\n    this.setSelectionStyle = function(style) {\n        if (this.$selectionStyle == style) return;\n\n        this.$selectionStyle = style;\n        this.onSelectionChange();\n        this._dispatchEvent(\"changeSelectionStyle\", {data: style});\n    };\n\n    this.getSelectionStyle = function() {\n        return this.$selectionStyle;\n    };\n\n    this.$highlightActiveLine = true;\n    this.setHighlightActiveLine = function(shouldHighlight) {\n        if (this.$highlightActiveLine == shouldHighlight) return;\n\n        this.$highlightActiveLine = shouldHighlight;\n        this.$updateHighlightActiveLine();\n    };\n\n    this.getHighlightActiveLine = function() {\n        return this.$highlightActiveLine;\n    };\n\n    this.$highlightSelectedWord = true;\n    this.setHighlightSelectedWord = function(shouldHighlight) {\n        if (this.$highlightSelectedWord == shouldHighlight)\n            return;\n\n        this.$highlightSelectedWord = shouldHighlight;\n        if (shouldHighlight)\n            this.mode.highlightSelection(this);\n        else\n            this.mode.clearSelectionHighlight(this);\n    };\n\n    this.getHighlightSelectedWord = function() {\n        return this.$highlightSelectedWord;\n    };\n\n    this.setShowInvisibles = function(showInvisibles) {\n        if (this.getShowInvisibles() == showInvisibles)\n            return;\n\n        this.renderer.setShowInvisibles(showInvisibles);\n    };\n\n    this.getShowInvisibles = function() {\n        return this.renderer.getShowInvisibles();\n    };\n\n    this.setShowPrintMargin = function(showPrintMargin) {\n        this.renderer.setShowPrintMargin(showPrintMargin);\n    };\n\n    this.getShowPrintMargin = function() {\n        return this.renderer.getShowPrintMargin();\n    };\n\n    this.setPrintMarginColumn = function(showPrintMargin) {\n        this.renderer.setPrintMarginColumn(showPrintMargin);\n    };\n\n    this.getPrintMarginColumn = function() {\n        return this.renderer.getPrintMarginColumn();\n    };\n\n    this.$readOnly = false;\n    this.setReadOnly = function(readOnly) {\n        this.$readOnly = readOnly;\n    };\n\n    this.getReadOnly = function() {\n        return this.$readOnly;\n    };\n\n    this.removeRight = function() {\n        if (this.$readOnly)\n            return;\n\n        if (this.selection.isEmpty()) {\n            this.selection.selectRight();\n        }\n        this.session.remove(this.getSelectionRange())\n        this.clearSelection();\n    };\n\n    this.removeLeft = function() {\n        if (this.$readOnly)\n            return;\n\n        if (this.selection.isEmpty())\n            this.selection.selectLeft();\n\n        this.session.remove(this.getSelectionRange());\n        this.clearSelection();\n    };\n    \n    this.removeWordRight = function() {\n        if (this.$readOnly)\n            return;\n\n        if (this.selection.isEmpty())\n            this.selection.selectWordRight();\n\n        this.session.remove(this.getSelectionRange());\n        this.clearSelection();\n    };\n    \n    this.removeWordLeft = function() {\n        if (this.$readOnly)\n            return;\n\n        if (this.selection.isEmpty())\n            this.selection.selectWordLeft();\n\n        this.session.remove(this.getSelectionRange());\n        this.clearSelection();\n    };\n    \n    this.removeToLineStart = function() {\n        if (this.$readOnly)\n            return;\n\n        if (this.selection.isEmpty())\n            this.selection.selectLineStart();\n\n        this.session.remove(this.getSelectionRange());\n        this.clearSelection();\n    };\n    \n    this.removeToLineEnd = function() {\n        if (this.$readOnly)\n            return;\n\n        if (this.selection.isEmpty())\n            this.selection.selectLineEnd();\n\n        this.session.remove(this.getSelectionRange());\n        this.clearSelection();\n    };\n\n    this.splitLine = function() {\n        if (this.$readOnly)\n            return;\n        \n        if (!this.selection.isEmpty()) {\n            this.session.remove(this.getSelectionRange());\n            this.clearSelection();\n        }\n    \n        var cursor = this.getCursorPosition();\n        this.insert(\"\\n\");\n        this.moveCursorToPosition(cursor);\n    };\n    \n    this.transposeLetters = function() {\n        if (this.$readOnly)\n            return;\n        \n        if (!this.selection.isEmpty()) {\n            return;\n        }\n    \n        var cursor = this.getCursorPosition();\n        var column = cursor.column;\n        if (column == 0)\n            return;\n        \n        var line = this.session.getLine(cursor.row);\n        if (column < line.length) {\n            var swap = line.charAt(column) + line.charAt(column-1);\n            var range = new Range(cursor.row, column-1, cursor.row, column+1)\n        }\n        else {\n            var swap = line.charAt(column-1) + line.charAt(column-2);\n            var range = new Range(cursor.row, column-2, cursor.row, column)\n        }\n        this.session.replace(range, swap);\n    };\n    \n    this.indent = function() {\n        if (this.$readOnly)\n            return;\n\n        var session = this.session;\n        var range = this.getSelectionRange();\n\n        if (range.start.row < range.end.row || range.start.column < range.end.column) {\n            var rows = this.$getSelectedRows();\n            session.indentRows(rows.first, rows.last, \"\\t\");\n        } else {\n            var indentString;\n\n            if (this.session.getUseSoftTabs()) {\n                var size        = session.getTabSize(),\n                    position    = this.getCursorPosition(),\n                    column      = session.documentToScreenColumn(position.row, position.column),\n                    count       = (size - column % size);\n\n                indentString = lang.stringRepeat(\" \", count);\n            } else\n                indentString = \"\\t\";\n            return this.onTextInput(indentString);\n        }\n    };\n\n    this.blockOutdent = function() {\n        if (this.$readOnly)\n            return;\n\n        var selection = this.session.getSelection();\n        this.session.outdentRows(selection.getRange());\n    };\n\n    this.toggleCommentLines = function() {\n        if (this.$readOnly)\n            return;\n\n        var state = this.bgTokenizer.getState(this.getCursorPosition().row);\n        var rows = this.$getSelectedRows()\n        this.mode.toggleCommentLines(state, this.session, rows.first, rows.last);\n    };\n\n    this.removeLines = function() {\n        if (this.$readOnly)\n            return;\n\n        var rows = this.$getSelectedRows();\n        this.session.remove(new Range(rows.first, 0, rows.last+1, 0));\n        this.clearSelection();\n    };\n\n    this.moveLinesDown = function() {\n        if (this.$readOnly)\n            return;\n\n        this.$moveLines(function(firstRow, lastRow) {\n            return this.session.moveLinesDown(firstRow, lastRow);\n        });\n    };\n\n    this.moveLinesUp = function() {\n        if (this.$readOnly)\n            return;\n\n        this.$moveLines(function(firstRow, lastRow) {\n            return this.session.moveLinesUp(firstRow, lastRow);\n        });\n    };\n\n    this.moveText = function(range, toPosition) {\n        if (this.$readOnly)\n            return null;\n\n        return this.session.moveText(range, toPosition);\n    };\n\n    this.copyLinesUp = function() {\n        if (this.$readOnly)\n            return;\n\n        this.$moveLines(function(firstRow, lastRow) {\n            this.session.duplicateLines(firstRow, lastRow);\n            return 0;\n        });\n    };\n\n    this.copyLinesDown = function() {\n        if (this.$readOnly)\n            return;\n\n        this.$moveLines(function(firstRow, lastRow) {\n            return this.session.duplicateLines(firstRow, lastRow);\n        });\n    };\n\n\n    this.$moveLines = function(mover) {\n        var rows = this.$getSelectedRows();\n\n        var linesMoved = mover.call(this, rows.first, rows.last);\n\n        var selection = this.selection;\n        selection.setSelectionAnchor(rows.last+linesMoved+1, 0);\n        selection.$moveSelection(function() {\n            selection.moveCursorTo(rows.first+linesMoved, 0);\n        });\n    };\n\n    this.$getSelectedRows = function() {\n        var range = this.getSelectionRange().collapseRows();\n\n        return {\n            first: range.start.row,\n            last: range.end.row\n        };\n    };\n\n    this.onCompositionStart = function(text) {\n        this.renderer.showComposition(this.getCursorPosition());\n    };\n\n    this.onCompositionUpdate = function(text) {\n        this.renderer.setCompositionText(text);\n    };\n\n    this.onCompositionEnd = function() {\n        this.renderer.hideComposition();\n    };\n\n\n    this.getFirstVisibleRow = function() {\n        return this.renderer.getFirstVisibleRow();\n    };\n\n    this.getLastVisibleRow = function() {\n        return this.renderer.getLastVisibleRow();\n    };\n\n    this.isRowVisible = function(row) {\n        return (row >= this.getFirstVisibleRow() && row <= this.getLastVisibleRow());\n    };\n\n    this.$getVisibleRowCount = function() {\n        return this.renderer.getScrollBottomRow() - this.renderer.getScrollTopRow() + 1;\n    };\n\n    this.$getPageDownRow = function() {\n        return this.renderer.getScrollBottomRow();\n    };\n\n    this.$getPageUpRow = function() {\n        var firstRow = this.renderer.getScrollTopRow();\n        var lastRow = this.renderer.getScrollBottomRow();\n\n        return firstRow - (lastRow - firstRow);\n    };\n\n    this.selectPageDown = function() {\n        var row = this.$getPageDownRow() + Math.floor(this.$getVisibleRowCount() / 2);\n\n        this.scrollPageDown();\n\n        var selection = this.getSelection();\n        var leadScreenPos = this.session.documentToScreenPosition(selection.getSelectionLead());\n        var dest = this.session.screenToDocumentPosition(row, leadScreenPos.column);\n        selection.selectTo(dest.row, dest.column);\n    };\n\n    this.selectPageUp = function() {\n        var visibleRows = this.renderer.getScrollTopRow() - this.renderer.getScrollBottomRow();\n        var row = this.$getPageUpRow() + Math.round(visibleRows / 2);\n\n        this.scrollPageUp();\n\n        var selection = this.getSelection();\n        var leadScreenPos = this.session.documentToScreenPosition(selection.getSelectionLead());\n        var dest = this.session.screenToDocumentPosition(row, leadScreenPos.column);\n        selection.selectTo(dest.row, dest.column);\n    };\n\n    this.gotoPageDown = function() {\n        var row = this.$getPageDownRow();\n        var column = this.getCursorPositionScreen().column;\n\n        this.scrollToRow(row);\n        this.getSelection().moveCursorToScreen(row, column);\n    };\n\n    this.gotoPageUp = function() {\n        var row = this.$getPageUpRow();\n        var column = this.getCursorPositionScreen().column;\n\n       this.scrollToRow(row);\n       this.getSelection().moveCursorToScreen(row, column);\n    };\n\n    this.scrollPageDown = function() {\n        this.scrollToRow(this.$getPageDownRow());\n    };\n\n    this.scrollPageUp = function() {\n        this.renderer.scrollToRow(this.$getPageUpRow());\n    };\n\n    this.scrollToRow = function(row) {\n        this.renderer.scrollToRow(row);\n    };\n\n    this.scrollToLine = function(line, center) {\n        this.renderer.scrollToLine(line, center);\n    };\n    \n    this.centerSelection = function() {\n        var range = this.getSelectionRange();\n        var line = Math.floor(range.start.row + (range.end.row - range.start.row) / 2);\n        this.renderer.scrollToLine(line, true);\n    };\n\n    this.getCursorPosition = function() {\n        return this.selection.getCursor();\n    };\n\n    this.getCursorPositionScreen = function() {\n        return this.session.documentToScreenPosition(this.getCursorPosition());\n    };\n\n    this.getSelectionRange = function() {\n        return this.selection.getRange();\n    };\n\n\n    this.selectAll = function() {\n        this.$blockScrolling += 1;\n        this.selection.selectAll();\n        this.$blockScrolling -= 1;\n    };\n    \n    this.clearSelection = function() {\n        this.selection.clearSelection();\n    };\n\n    this.moveCursorTo = function(row, column) {\n        this.selection.moveCursorTo(row, column);\n    };\n\n    this.moveCursorToPosition = function(pos) {\n        this.selection.moveCursorToPosition(pos);\n    };\n\n\n    this.gotoLine = function(lineNumber, row) {\n        this.selection.clearSelection();\n\n        this.$blockScrolling += 1;\n        this.moveCursorTo(lineNumber-1, row || 0);\n        this.$blockScrolling -= 1;\n\n        if (!this.isRowVisible(this.getCursorPosition().row)) {\n            this.scrollToLine(lineNumber, true);\n        }\n    },\n\n    this.navigateTo = function(row, column) {\n        this.clearSelection();\n        this.moveCursorTo(row, column);\n    };\n\n    this.navigateUp = function(times) {\n        this.selection.clearSelection();\n        times = times || 1;\n        this.selection.moveCursorBy(-times, 0);\n    };\n\n    this.navigateDown = function(times) {\n        this.selection.clearSelection();\n        times = times || 1;\n        this.selection.moveCursorBy(times, 0);\n    };\n\n    this.navigateLeft = function(times) {\n        if (!this.selection.isEmpty()) {\n            var selectionStart = this.getSelectionRange().start;\n            this.moveCursorToPosition(selectionStart);\n        }\n        else {\n            times = times || 1;\n            while (times--) {\n                this.selection.moveCursorLeft();\n            }\n        }\n        this.clearSelection();\n    };\n\n    this.navigateRight = function(times) {\n        if (!this.selection.isEmpty()) {\n            var selectionEnd = this.getSelectionRange().end;\n            this.moveCursorToPosition(selectionEnd);\n        }\n        else {\n            times = times || 1;\n            while (times--) {\n                this.selection.moveCursorRight();\n            }\n        }\n        this.clearSelection();\n    };\n\n    this.navigateLineStart = function() {\n        this.selection.moveCursorLineStart();\n        this.clearSelection();\n    };\n\n    this.navigateLineEnd = function() {\n        this.selection.moveCursorLineEnd();\n        this.clearSelection();\n    };\n\n    this.navigateFileEnd = function() {\n        this.selection.moveCursorFileEnd();\n        this.clearSelection();\n    };\n\n    this.navigateFileStart = function() {\n        this.selection.moveCursorFileStart();\n        this.clearSelection();\n    };\n\n    this.navigateWordRight = function() {\n        this.selection.moveCursorWordRight();\n        this.clearSelection();\n    };\n\n    this.navigateWordLeft = function() {\n        this.selection.moveCursorWordLeft();\n        this.clearSelection();\n    };\n\n    this.replace = function(replacement, options) {\n        if (options)\n            this.$search.set(options);\n\n        var range = this.$search.find(this.session);\n        this.$tryReplace(range, replacement);\n        if (range !== null)\n            this.selection.setSelectionRange(range);\n    },\n\n    this.replaceAll = function(replacement, options) {\n        if (options) {\n            this.$search.set(options);\n        }\n\n        var ranges = this.$search.findAll(this.session);\n        if (!ranges.length)\n            return;\n\n        var selection = this.getSelectionRange();\n        this.clearSelection();\n        this.selection.moveCursorTo(0, 0);\n\n        this.$blockScrolling += 1;\n        for (var i = ranges.length - 1; i >= 0; --i)\n            this.$tryReplace(ranges[i], replacement);\n        \n        this.selection.setSelectionRange(selection);\n        this.$blockScrolling -= 1;\n    },\n\n    this.$tryReplace = function(range, replacement) {\n        var input = this.session.getTextRange(range);\n        var replacement = this.$search.replace(input, replacement);\n        if (replacement !== null) {\n            range.end = this.session.replace(range, replacement);\n            return range;\n        } else {\n            return null;\n        }\n    };\n\n    this.getLastSearchOptions = function() {\n        return this.$search.getOptions();\n    };\n\n    this.find = function(needle, options) {\n        this.clearSelection();\n        options = options || {};\n        options.needle = needle;\n        this.$search.set(options);\n        this.$find();\n    },\n\n    this.findNext = function(options) {\n        options = options || {};\n        if (typeof options.backwards == \"undefined\")\n            options.backwards = false;\n        this.$search.set(options);\n        this.$find();\n    };\n\n    this.findPrevious = function(options) {\n        options = options || {};\n        if (typeof options.backwards == \"undefined\")\n            options.backwards = true;\n        this.$search.set(options);\n        this.$find();\n    };\n\n    this.$find = function(backwards) {\n        if (!this.selection.isEmpty()) {\n            this.$search.set({needle: this.session.getTextRange(this.getSelectionRange())});\n        }\n\n        if (typeof backwards != \"undefined\")\n            this.$search.set({backwards: backwards});\n\n        var range = this.$search.find(this.session);\n        if (range) {\n            this.gotoLine(range.end.row+1, range.end.column);\n            this.selection.setSelectionRange(range);\n        }\n    };\n\n    this.undo = function() {\n        this.session.getUndoManager().undo();\n    };\n\n    this.redo = function() {\n        this.session.getUndoManager().redo();\n    };\n\n}).call(Editor.prototype);\n\n\nexports.Editor = Editor;\n});\n/* ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Ajax.org Code Editor (ACE).\n *\n * The Initial Developer of the Original Code is\n * Ajax.org B.V.\n * Portions created by the Initial Developer are Copyright (C) 2010\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *      Fabian Jakobs <fabian AT ajax DOT org>\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('pilot/event', function(require, exports, module) {\n\nvar keys = require(\"pilot/keys\");\nvar useragent = require(\"pilot/useragent\");\nvar dom = require(\"pilot/dom\");\n\nexports.addListener = function(elem, type, callback) {\n    if (elem.addEventListener) {\n        return elem.addEventListener(type, callback, false);\n    }\n    if (elem.attachEvent) {\n        var wrapper = function() {\n            callback(window.event);\n        };\n        callback._wrapper = wrapper;\n        elem.attachEvent(\"on\" + type, wrapper);\n    }\n};\n\nexports.removeListener = function(elem, type, callback) {\n    if (elem.removeEventListener) {\n        return elem.removeEventListener(type, callback, false);\n    }\n    if (elem.detachEvent) {\n        elem.detachEvent(\"on\" + type, callback._wrapper || callback);\n    }\n};\n\n/**\n* Prevents propagation and clobbers the default action of the passed event\n*/\nexports.stopEvent = function(e) {\n    exports.stopPropagation(e);\n    exports.preventDefault(e);\n    return false;\n};\n\nexports.stopPropagation = function(e) {\n    if (e.stopPropagation)\n        e.stopPropagation();\n    else\n        e.cancelBubble = true;\n};\n\nexports.preventDefault = function(e) {\n    if (e.preventDefault)\n        e.preventDefault();\n    else\n        e.returnValue = false;\n};\n\nexports.getDocumentX = function(e) {\n    if (e.clientX) {        \n        return e.clientX + dom.getPageScrollLeft();\n    } else {\n        return e.pageX;\n    }\n};\n\nexports.getDocumentY = function(e) {\n    if (e.clientY) {\n        return e.clientY + dom.getPageScrollTop();\n    } else {\n        return e.pageY;\n    }\n};\n\n/**\n * @return {Number} 0 for left button, 1 for middle button, 2 for right button\n */\nexports.getButton = function(e) {\n    if (e.type == \"dblclick\")\n        return 0;\n    else if (e.type == \"contextmenu\")\n        return 2;\n        \n    // DOM Event\n    if (e.preventDefault) {\n        return e.button;\n    }\n    // old IE\n    else {\n        return {1:0, 2:2, 4:1}[e.button];\n    }\n};\n\nif (document.documentElement.setCapture) {\n    exports.capture = function(el, eventHandler, releaseCaptureHandler) {\n        function onMouseMove(e) {\n            eventHandler(e);\n            return exports.stopPropagation(e);\n        }\n\n        function onReleaseCapture(e) {\n            eventHandler && eventHandler(e);\n            releaseCaptureHandler && releaseCaptureHandler();\n\n            exports.removeListener(el, \"mousemove\", eventHandler);\n            exports.removeListener(el, \"mouseup\", onReleaseCapture);\n            exports.removeListener(el, \"losecapture\", onReleaseCapture);\n\n            el.releaseCapture();\n        }\n\n        exports.addListener(el, \"mousemove\", eventHandler);\n        exports.addListener(el, \"mouseup\", onReleaseCapture);\n        exports.addListener(el, \"losecapture\", onReleaseCapture);\n        el.setCapture();\n    };\n}\nelse {\n    exports.capture = function(el, eventHandler, releaseCaptureHandler) {\n        function onMouseMove(e) {\n            eventHandler(e);\n            e.stopPropagation();\n        }\n\n        function onMouseUp(e) {\n            eventHandler && eventHandler(e);\n            releaseCaptureHandler && releaseCaptureHandler();\n\n            document.removeEventListener(\"mousemove\", onMouseMove, true);\n            document.removeEventListener(\"mouseup\", onMouseUp, true);\n\n            e.stopPropagation();\n        }\n\n        document.addEventListener(\"mousemove\", onMouseMove, true);\n        document.addEventListener(\"mouseup\", onMouseUp, true);\n    };\n}\n\nexports.addMouseWheelListener = function(el, callback) {\n    var listener = function(e) {\n        if (e.wheelDelta !== undefined) {\n            if (e.wheelDeltaX !== undefined) {\n                e.wheelX = -e.wheelDeltaX / 8;\n                e.wheelY = -e.wheelDeltaY / 8;\n            } else {\n                e.wheelX = 0;\n                e.wheelY = -e.wheelDelta / 8;\n            }\n        }\n        else {\n            if (e.axis && e.axis == e.HORIZONTAL_AXIS) {\n                e.wheelX = (e.detail || 0) * 5;\n                e.wheelY = 0;\n            } else {\n                e.wheelX = 0;\n                e.wheelY = (e.detail || 0) * 5;\n            }\n        }\n        callback(e);\n    };\n    exports.addListener(el, \"DOMMouseScroll\", listener);\n    exports.addListener(el, \"mousewheel\", listener);\n};\n\nexports.addMultiMouseDownListener = function(el, button, count, timeout, callback) {\n    var clicks = 0;\n    var startX, startY;\n\n    var listener = function(e) {\n        clicks += 1;\n        if (clicks == 1) {\n            startX = e.clientX;\n            startY = e.clientY;\n\n            setTimeout(function() {\n                clicks = 0;\n            }, timeout || 600);\n        }\n\n        if (exports.getButton(e) != button\n          || Math.abs(e.clientX - startX) > 5 || Math.abs(e.clientY - startY) > 5)\n            clicks = 0;\n\n        if (clicks == count) {\n            clicks = 0;\n            callback(e);\n        }\n        return exports.preventDefault(e);\n    };\n\n    exports.addListener(el, \"mousedown\", listener);\n    useragent.isIE && exports.addListener(el, \"dblclick\", listener);\n};\n\nfunction normalizeCommandKeys(callback, e, keyCode) {\n    var hashId = 0;\n    if (useragent.isOpera && useragent.isMac) {\n        hashId = 0 | (e.metaKey ? 1 : 0) | (e.altKey ? 2 : 0)\n            | (e.shiftKey ? 4 : 0) | (e.ctrlKey ? 8 : 0);\n    } else {\n        hashId = 0 | (e.ctrlKey ? 1 : 0) | (e.altKey ? 2 : 0)\n            | (e.shiftKey ? 4 : 0) | (e.metaKey ? 8 : 0);\n    }\n\n    if (keyCode in keys.MODIFIER_KEYS) {\n        switch (keys.MODIFIER_KEYS[keyCode]) {\n            case \"Alt\":\n                hashId = 2;\n                break;\n            case \"Shift\":\n                hashId = 4;\n                break\n            case \"Ctrl\":\n                hashId = 1;\n                break;\n            default:\n                hashId = 8;\n                break;\n        }\n        keyCode = 0;\n    }\n\n    if (hashId & 8 && (keyCode == 91 || keyCode == 93)) {\n        keyCode = 0;\n    }\n\n    // If there is no hashID and the keyCode is not a function key, then\n    // we don't call the callback as we don't handle a command key here\n    // (it's a normal key/character input).\n    if (hashId == 0 && !(keyCode in keys.FUNCTION_KEYS)) {\n        return false;\n    }\n\n    return callback(e, hashId, keyCode);\n}\n\nexports.addCommandKeyListener = function(el, callback) {\n    var addListener = exports.addListener;\n    if (useragent.isOldGecko) {\n        // Old versions of Gecko aka. Firefox < 4.0 didn't repeat the keydown\n        // event if the user pressed the key for a longer time. Instead, the\n        // keydown event was fired once and later on only the keypress event.\n        // To emulate the 'right' keydown behavior, the keyCode of the initial\n        // keyDown event is stored and in the following keypress events the\n        // stores keyCode is used to emulate a keyDown event.\n        var lastKeyDownKeyCode = null;\n        addListener(el, \"keydown\", function(e) {\n            lastKeyDownKeyCode = e.keyCode;\n        });\n        addListener(el, \"keypress\", function(e) {\n            return normalizeCommandKeys(callback, e, lastKeyDownKeyCode);\n        });\n    } else {\n        var lastDown = null;\n\n        addListener(el, \"keydown\", function(e) {\n            lastDown = e.keyIdentifier || e.keyCode;\n            return normalizeCommandKeys(callback, e, e.keyCode);\n        });\n\n        // repeated keys are fired as keypress and not keydown events\n        if (useragent.isMac && useragent.isOpera) {\n            addListener(el, \"keypress\", function(e) {\n                var keyId = e.keyIdentifier || e.keyCode;\n                if (lastDown !== keyId) {\n                    return normalizeCommandKeys(callback, e, e.keyCode);\n                } else {\n                    lastDown = null;\n                }\n            });\n        }\n    }\n};\n\n});\n/* vim:ts=4:sts=4:sw=4:\n * ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Ajax.org Code Editor (ACE).\n *\n * The Initial Developer of the Original Code is\n * Ajax.org B.V.\n * Portions created by the Initial Developer are Copyright (C) 2010\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *      Fabian Jakobs <fabian AT ajax DOT org>\n *      Mihai Sucan <mihai AT sucan AT gmail ODT com>\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('pilot/dom', function(require, exports, module) {\n\nvar XHTML_NS = \"http://www.w3.org/1999/xhtml\";\n\nexports.createElement = function(tag, ns) {\n    return document.createElementNS ?\n           document.createElementNS(ns || XHTML_NS, tag) :\n           document.createElement(tag);\n};\n\nexports.setText = function(elem, text) {\n    if (elem.innerText !== undefined) {\n        elem.innerText = text;\n    }\n    if (elem.textContent !== undefined) {\n        elem.textContent = text;\n    }\n};\n\nif (!document.documentElement.classList) {\n    exports.hasCssClass = function(el, name) {\n        var classes = el.className.split(/\\s+/g);\n        return classes.indexOf(name) !== -1;\n    };\n\n    /**\n    * Add a CSS class to the list of classes on the given node\n    */\n    exports.addCssClass = function(el, name) {\n        if (!exports.hasCssClass(el, name)) {\n            el.className += \" \" + name;\n        }\n    };\n\n    /**\n    * Remove a CSS class from the list of classes on the given node\n    */\n    exports.removeCssClass = function(el, name) {\n        var classes = el.className.split(/\\s+/g);\n        while (true) {\n            var index = classes.indexOf(name);\n            if (index == -1) {\n                break;\n            }\n            classes.splice(index, 1);\n        }\n        el.className = classes.join(\" \");\n    };\n\n    exports.toggleCssClass = function(el, name) {\n        var classes = el.className.split(/\\s+/g), add = true;\n        while (true) {\n            var index = classes.indexOf(name);\n            if (index == -1) {\n                break;\n            }\n            add = false;\n            classes.splice(index, 1);\n        }\n        if(add)\n            classes.push(name);\n\n        el.className = classes.join(\" \");\n        return add;\n    };\n} else {\n    exports.hasCssClass = function(el, name) {\n        return el.classList.contains(name);\n    };\n\n    exports.addCssClass = function(el, name) {\n        el.classList.add(name);\n    };\n\n    exports.removeCssClass = function(el, name) {\n        el.classList.remove(name);\n    };\n\n    exports.toggleCssClass = function(el, name) {\n        return el.classList.toggle(name);\n    };\n}\n\n/**\n * Add or remove a CSS class from the list of classes on the given node\n * depending on the value of <tt>include</tt>\n */\nexports.setCssClass = function(node, className, include) {\n    if (include) {\n        exports.addCssClass(node, className);\n    } else {\n        exports.removeCssClass(node, className);\n    }\n};\n\nexports.importCssString = function(cssText, doc){\n    doc = doc || document;\n\n    if (doc.createStyleSheet) {\n        var sheet = doc.createStyleSheet();\n        sheet.cssText = cssText;\n    }\n    else {\n        var style = doc.createElementNS ?\n                    doc.createElementNS(XHTML_NS, \"style\") :\n                    doc.createElement(\"style\");\n\n        style.appendChild(doc.createTextNode(cssText));\n\n        var head = doc.getElementsByTagName(\"head\")[0] || doc.documentElement;\n        head.appendChild(style);\n    }\n};\n\nexports.getInnerWidth = function(element) {\n    return (parseInt(exports.computedStyle(element, \"paddingLeft\"))\n            + parseInt(exports.computedStyle(element, \"paddingRight\")) + element.clientWidth);\n};\n\nexports.getInnerHeight = function(element) {\n    return (parseInt(exports.computedStyle(element, \"paddingTop\"))\n            + parseInt(exports.computedStyle(element, \"paddingBottom\")) + element.clientHeight);\n};\n\nif (window.pageYOffset !== undefined) {\n    exports.getPageScrollTop = function() {\n        return window.pageYOffset;\n    };\n\n    exports.getPageScrollLeft = function() {\n        return window.pageXOffset;\n    };\n}\nelse {\n    exports.getPageScrollTop = function() {\n        return document.body.scrollTop;\n    };\n\n    exports.getPageScrollLeft = function() {\n        return document.body.scrollLeft;\n    };\n}\n\nexports.computedStyle = function(element, style) {\n    if (window.getComputedStyle) {\n        return (window.getComputedStyle(element, \"\") || {})[style] || \"\";\n    }\n    else {\n        return element.currentStyle[style];\n    }\n};\n\nexports.scrollbarWidth = function() {\n\n    var inner = exports.createElement(\"p\");\n    inner.style.width = \"100%\";\n    inner.style.height = \"200px\";\n\n    var outer = exports.createElement(\"div\");\n    var style = outer.style;\n\n    style.position = \"absolute\";\n    style.left = \"-10000px\";\n    style.overflow = \"hidden\";\n    style.width = \"200px\";\n    style.height = \"150px\";\n\n    outer.appendChild(inner);\n\n    var body = document.body || document.documentElement;\n    body.appendChild(outer);\n\n    var noScrollbar = inner.offsetWidth;\n\n    style.overflow = \"scroll\";\n    var withScrollbar = inner.offsetWidth;\n\n    if (noScrollbar == withScrollbar) {\n        withScrollbar = outer.clientWidth;\n    }\n\n    body.removeChild(outer);\n\n    return noScrollbar-withScrollbar;\n};\n\n/**\n * Optimized set innerHTML. This is faster than plain innerHTML if the element\n * already contains a lot of child elements.\n *\n * See http://blog.stevenlevithan.com/archives/faster-than-innerhtml for details\n */\nexports.setInnerHtml = function(el, innerHtml) {\n    var element = el.cloneNode(false);//document.createElement(\"div\");\n    element.innerHTML = innerHtml;\n    el.parentNode.replaceChild(element, el);\n    return element;\n};\n\nexports.setInnerText = function(el, innerText) {\n    if (document.body && \"textContent\" in document.body)\n        el.textContent = innerText;\n    else\n        el.innerText = innerText;\n\n};\n\nexports.getInnerText = function(el) {\n    if (document.body && \"textContent\" in document.body)\n        return el.textContent;\n    else\n         return el.innerText || el.textContent || \"\";\n};\n\nexports.getParentWindow = function(document) {\n    return document.defaultView || document.parentWindow;\n};\n\nexports.getSelectionStart = function(textarea) {\n    // TODO IE\n    var start;\n    try {\n        start = textarea.selectionStart || 0;\n    } catch (e) {\n        start = 0;\n    }\n    return start;\n};\n\nexports.setSelectionStart = function(textarea, start) {\n    // TODO IE\n    return textarea.selectionStart = start;\n};\n\nexports.getSelectionEnd = function(textarea) {\n    // TODO IE\n    var end;\n    try {\n        end = textarea.selectionEnd || 0;\n    } catch (e) {\n        end = 0;\n    }\n    return end;\n};\n\nexports.setSelectionEnd = function(textarea, end) {\n    // TODO IE\n    return textarea.selectionEnd = end;\n};\n\n});\n/* vim:ts=4:sts=4:sw=4:\n * ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Ajax.org Code Editor (ACE).\n *\n * The Initial Developer of the Original Code is\n * Ajax.org B.V.\n * Portions created by the Initial Developer are Copyright (C) 2010\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *      Fabian Jakobs <fabian AT ajax DOT org>\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('ace/keyboard/textinput', function(require, exports, module) {\n\nvar event = require(\"pilot/event\");\nvar useragent = require(\"pilot/useragent\");\nvar dom = require(\"pilot/dom\");\n\nvar TextInput = function(parentNode, host) {\n\n    var text = dom.createElement(\"textarea\");\n    text.style.left = \"-10000px\";\n    parentNode.appendChild(text);\n\n    var PLACEHOLDER = String.fromCharCode(0);\n    sendText();\n\n    var inCompostion = false;\n    var copied = false;\n    var tempStyle = '';\n\n    function sendText(valueToSend) {\n        if (!copied) {\n            var value = valueToSend || text.value;\n            if (value) {\n                if (value.charCodeAt(value.length-1) == PLACEHOLDER.charCodeAt(0)) {\n                    value = value.slice(0, -1);\n                    if (value)\n                        host.onTextInput(value);\n                } else\n                    host.onTextInput(value);\n            }\n        }\n        copied = false;\n\n        // Safari doesn't fire copy events if no text is selected\n        text.value = PLACEHOLDER;\n        text.select();\n    }\n\n    var onTextInput = function(e) {\n        if (useragent.isIE && text.value.charCodeAt(0) > 128) return;\n        setTimeout(function() {\n            if (!inCompostion)\n                sendText();\n        }, 0);\n    };\n\n    var onCompositionStart = function(e) {\n        inCompostion = true;\n        if (!useragent.isIE) {\n            sendText();\n            text.value = \"\";\n        };\n        host.onCompositionStart();\n        if (!useragent.isGecko) setTimeout(onCompositionUpdate, 0);\n    };\n\n    var onCompositionUpdate = function() {\n        if (!inCompostion) return;\n        host.onCompositionUpdate(text.value);\n    };\n\n    var onCompositionEnd = function() {\n        inCompostion = false;\n        host.onCompositionEnd();\n        setTimeout(function () {\n            sendText();\n        }, 0);\n    };\n\n    var onCopy = function(e) {\n        copied = true;\n        var copyText = host.getCopyText();\n        if(copyText)\n            text.value = copyText;\n        else\n            e.preventDefault();\n        text.select();\n        setTimeout(function () {\n            sendText();\n        }, 0);\n    };\n\n    var onCut = function(e) {\n        copied = true;\n        var copyText = host.getCopyText();\n        if(copyText) {\n            text.value = copyText;\n            host.onCut();\n        } else\n            e.preventDefault();\n        text.select();\n        setTimeout(function () {\n            sendText();\n        }, 0);\n    };\n\n    event.addCommandKeyListener(text, host.onCommandKey.bind(host));\n    event.addListener(text, \"keypress\", onTextInput);\n    if (useragent.isIE) {\n        var keytable = { 13:1, 27:1 };\n        event.addListener(text, \"keyup\", function (e) {\n            if (inCompostion && (!text.value || keytable[e.keyCode]))\n                setTimeout(onCompositionEnd, 0);\n            if ((text.value.charCodeAt(0)|0) < 129) {\n                return;\n            };\n            inCompostion ? onCompositionUpdate() : onCompositionStart();\n        });\n    };\n    event.addListener(text, \"textInput\", onTextInput);\n    event.addListener(text, \"paste\", function(e) {\n        // Some browsers support the event.clipboardData API. Use this to get\n        // the pasted content which increases speed if pasting a lot of lines.\n        if (e.clipboardData && e.clipboardData.getData) {\n            sendText(e.clipboardData.getData(\"text/plain\"));\n            e.preventDefault();\n        } else\n        // If a browser doesn't support any of the things above, use the regular\n        // method to detect the pasted input.\n        {\n            onTextInput();\n        }\n    });\n    if (!useragent.isIE) {\n        event.addListener(text, \"propertychange\", onTextInput);\n    };\n\n    if (useragent.isIE) {\n        event.addListener(text, \"beforecopy\", function(e) {        \n            var copyText = host.getCopyText();\n            if(copyText)\n                clipboardData.setData(\"Text\", copyText);\n            else\n                e.preventDefault();\n        });\n        event.addListener(parentNode, \"keydown\", function(e) {\n            if (e.ctrlKey && e.keyCode == 88) {\n                var copyText = host.getCopyText();\n                if (copyText) {\n                    clipboardData.setData(\"Text\", copyText);\n                    host.onCut();\n                }\n                event.preventDefault(e)\n            }\n        });\n    }\n    else {\n        event.addListener(text, \"copy\", onCopy);\n        event.addListener(text, \"cut\", onCut);\n    }\n\n    event.addListener(text, \"compositionstart\", onCompositionStart);\n    if (useragent.isGecko) {\n        event.addListener(text, \"text\", onCompositionUpdate);\n    };\n    if (useragent.isWebKit) {\n        event.addListener(text, \"keyup\", onCompositionUpdate);\n    };\n    event.addListener(text, \"compositionend\", onCompositionEnd);\n\n    event.addListener(text, \"blur\", function() {\n        host.onBlur();\n    });\n\n    event.addListener(text, \"focus\", function() {\n        host.onFocus();\n        text.select();\n    });\n\n    this.focus = function() {\n        host.onFocus();\n        text.select();\n        text.focus();\n    };\n\n    this.blur = function() {\n        text.blur();\n    };\n\n    this.getElement = function() {\n        return text;\n    };\n\n    this.onContextMenu = function(mousePos, isEmpty){\n        if (mousePos) {\n            if(!tempStyle)\n                tempStyle = text.style.cssText;\n            text.style.cssText = 'position:fixed; z-index:1000;' +\n                    'left:' + (mousePos.x - 2) + 'px; top:' + (mousePos.y - 2) + 'px;'\n\n        }\n        if (isEmpty)\n            text.value='';\n    }\n\n    this.onContextMenuClose = function(){\n        setTimeout(function () {\n            if (tempStyle) {\n                text.style.cssText = tempStyle;\n                tempStyle = '';\n            }\n            sendText();\n        }, 0);\n    }\n};\n\nexports.TextInput = TextInput;\n});\n/* vim:ts=4:sts=4:sw=4:\n * ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Ajax.org Code Editor (ACE).\n *\n * The Initial Developer of the Original Code is\n * Ajax.org B.V.\n * Portions created by the Initial Developer are Copyright (C) 2010\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *      Fabian Jakobs <fabian AT ajax DOT org>\n *      Mihai Sucan <mihai DOT sucan AT gmail DOT com>\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('ace/mouse_handler', function(require, exports, module) {\n\nvar event = require(\"pilot/event\");\nvar dom = require(\"pilot/dom\");\n\nvar STATE_UNKNOWN = 0;\nvar STATE_SELECT = 1;\nvar STATE_DRAG = 2;\n\nvar DRAG_TIMER = 250; // milliseconds\nvar DRAG_OFFSET = 5; // pixels\n\nvar MouseHandler = function(editor) {\n    this.editor = editor;\n    event.addListener(editor.container, \"mousedown\", function(e) {\n        editor.focus();\n        return event.preventDefault(e);\n    });\n    event.addListener(editor.container, \"selectstart\", function(e) {\n        return event.preventDefault(e);\n    });\n    \n    var mouseTarget = editor.renderer.getMouseEventTarget();\n    event.addListener(mouseTarget, \"mousedown\", this.onMouseDown.bind(this));\n    event.addMultiMouseDownListener(mouseTarget, 0, 2, 500, this.onMouseDoubleClick.bind(this));\n    event.addMultiMouseDownListener(mouseTarget, 0, 3, 600, this.onMouseTripleClick.bind(this));\n    event.addMultiMouseDownListener(mouseTarget, 0, 4, 600, this.onMouseQuadClick.bind(this));\n    event.addMouseWheelListener(mouseTarget, this.onMouseWheel.bind(this));\n};\n\n(function() {\n\n    this.$scrollSpeed = 1;\n    this.setScrollSpeed = function(speed) {\n        this.$scrollSpeed = speed;\n    };\n    \n    this.getScrollSpeed = function() {\n        return this.$scrollSpeed;\n    };\n\n    this.$getEventPosition = function(e) {\n        var pageX = event.getDocumentX(e);\n        var pageY = event.getDocumentY(e);\n        var pos = this.editor.renderer.screenToTextCoordinates(pageX, pageY);\n        pos.row = Math.max(0, Math.min(pos.row, this.editor.session.getLength()-1));\n        return pos;\n    };\n\n    this.$distance = function(ax, ay, bx, by) {\n        return Math.sqrt(Math.pow(bx - ax, 2) + Math.pow(by - ay, 2));\n    };\n\n    this.onMouseDown = function(e) {\n        var pageX = event.getDocumentX(e);\n        var pageY = event.getDocumentY(e);\n        var pos = this.$getEventPosition(e);\n        var editor = this.editor;\n        var self = this;\n        var selectionRange = editor.getSelectionRange();\n        var selectionEmpty = selectionRange.isEmpty();\n        var state = STATE_UNKNOWN;\n        var inSelection = false;\n    \n        var button = event.getButton(e);\n        if (button !== 0) {\n            if (selectionEmpty) {\n                editor.moveCursorToPosition(pos);\n            }\n            if(button == 2) {\n                editor.textInput.onContextMenu({x: pageX, y: pageY}, selectionEmpty);\n                event.capture(editor.container, function(){}, editor.textInput.onContextMenuClose);\n            }\n            return;\n        } else {\n            inSelection = !editor.getReadOnly()\n                && !selectionEmpty\n                && selectionRange.contains(pos.row, pos.column);\n        }\n\n        if (!inSelection) {\n            // Directly pick STATE_SELECT, since the user is not clicking inside\n            // a selection.\n            onStartSelect(pos);\n        }\n\n        editor.renderer.scrollCursorIntoView();\n    \n        var mousePageX, mousePageY;\n        var overwrite = editor.getOverwrite();\n        var mousedownTime = (new Date()).getTime();\n        var dragCursor, dragRange;\n    \n        var onMouseSelection = function(e) {\n            mousePageX = event.getDocumentX(e);\n            mousePageY = event.getDocumentY(e);\n        };\n    \n        var onMouseSelectionEnd = function() {\n            clearInterval(timerId);\n            if (state == STATE_UNKNOWN)\n                onStartSelect(pos);\n            else if (state == STATE_DRAG)\n                onMouseDragSelectionEnd();\n                \n            self.$clickSelection = null;\n            state = STATE_UNKNOWN;\n        };\n\n        var onMouseDragSelectionEnd = function() {\n            dom.removeCssClass(editor.container, \"ace_dragging\");\n            editor.session.removeMarker(dragSelectionMarker);\n\n            if (!self.$clickSelection) {\n                if (!dragCursor) {\n                    editor.moveCursorToPosition(pos);\n                    editor.selection.clearSelection(pos.row, pos.column);\n                }\n            }\n\n            if (!dragCursor)\n                return;\n\n            if (dragRange.contains(dragCursor.row, dragCursor.column)) {\n                dragCursor = null;\n                return;\n            }\n\n            editor.clearSelection();\n            var newRange = editor.moveText(dragRange, dragCursor);\n            if (!newRange) {\n                dragCursor = null;\n                return;\n            }\n\n            editor.selection.setSelectionRange(newRange);\n        };\n    \n        var onSelectionInterval = function() {\n            if (mousePageX === undefined || mousePageY === undefined)\n                return;\n\n            if (state == STATE_UNKNOWN) {\n                var distance = self.$distance(pageX, pageY, mousePageX, mousePageY);\n                var time = (new Date()).getTime();\n\n                \n                if (distance > DRAG_OFFSET) {\n                    state = STATE_SELECT;\n                    var cursor = editor.renderer.screenToTextCoordinates(mousePageX, mousePageY);\n                    cursor.row = Math.max(0, Math.min(cursor.row, editor.session.getLength()-1));\n                    onStartSelect(cursor);\n                } else if ((time - mousedownTime) > DRAG_TIMER) {\n                    state = STATE_DRAG;\n                    dragRange = editor.getSelectionRange();\n                    var style = editor.getSelectionStyle();\n                    dragSelectionMarker = editor.session.addMarker(dragRange, \"ace_selection\", style);\n                    editor.clearSelection();\n                    dom.addCssClass(editor.container, \"ace_dragging\");\n                }\n\n            }\n            \n            if (state == STATE_DRAG)\n                onDragSelectionInterval();\n            else if (state == STATE_SELECT)\n                onUpdateSelectionInterval();\n        };\n    \n        function onStartSelect(pos) {\n            if (e.shiftKey)\n                editor.selection.selectToPosition(pos)\n            else {\n                if (!self.$clickSelection) {\n                    editor.moveCursorToPosition(pos);\n                    editor.selection.clearSelection(pos.row, pos.column);\n                }\n            }\n            state = STATE_SELECT;\n        }\n        \n        var onUpdateSelectionInterval = function() {\n            var cursor = editor.renderer.screenToTextCoordinates(mousePageX, mousePageY);\n            cursor.row = Math.max(0, Math.min(cursor.row, editor.session.getLength()-1));\n    \n            if (self.$clickSelection) {                \n                if (self.$clickSelection.contains(cursor.row, cursor.column)) {\n                    editor.selection.setSelectionRange(self.$clickSelection);\n                } else {\n                    if (self.$clickSelection.compare(cursor.row, cursor.column) == -1) {\n                        var anchor = self.$clickSelection.end;\n                    } else {\n                        var anchor = self.$clickSelection.start;\n                    }\n                    editor.selection.setSelectionAnchor(anchor.row, anchor.column);\n                    editor.selection.selectToPosition(cursor);\n                }\n            }\n            else {\n                editor.selection.selectToPosition(cursor);\n            }\n    \n            editor.renderer.scrollCursorIntoView();\n        };\n\n        var onDragSelectionInterval = function() {\n            dragCursor = editor.renderer.screenToTextCoordinates(mousePageX, mousePageY);\n            dragCursor.row = Math.max(0, Math.min(dragCursor.row,\n                                                  editor.session.getLength() - 1));\n\n            editor.moveCursorToPosition(dragCursor);\n        };\n\n        event.capture(editor.container, onMouseSelection, onMouseSelectionEnd);\n        var timerId = setInterval(onSelectionInterval, 20);\n    \n        return event.preventDefault(e);\n    };\n    \n    this.onMouseDoubleClick = function(e) {\n        var pos = this.$getEventPosition(e);\n        this.editor.moveCursorToPosition(pos);\n        this.editor.selection.selectWord();\n        this.$clickSelection = this.editor.getSelectionRange();\n    };\n    \n    this.onMouseTripleClick = function(e) {\n        var pos = this.$getEventPosition(e);\n        this.editor.moveCursorToPosition(pos);\n        this.editor.selection.selectLine();\n        this.$clickSelection = this.editor.getSelectionRange();\n    };\n    \n    this.onMouseQuadClick = function(e) {\n        this.editor.selectAll();\n        this.$clickSelection = this.editor.getSelectionRange();\n    };\n    \n    this.onMouseWheel = function(e) {\n        var speed = this.$scrollSpeed * 2;\n    \n        this.editor.renderer.scrollBy(e.wheelX * speed, e.wheelY * speed);\n        return event.preventDefault(e);\n    };\n\n\n}).call(MouseHandler.prototype);\n\nexports.MouseHandler = MouseHandler;\n});\n/* ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Ajax.org Code Editor (ACE).\n *\n * The Initial Developer of the Original Code is\n * Ajax.org B.V.\n * Portions created by the Initial Developer are Copyright (C) 2010\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *      Fabian Jakobs <fabian AT ajax DOT org>\n *      Julian Viereck <julian.viereck@gmail.com>\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('ace/keyboard/keybinding', function(require, exports, module) {\n\nvar useragent = require(\"pilot/useragent\");\nvar keyUtil  = require(\"pilot/keys\");\nvar event = require(\"pilot/event\");\nvar settings  = require(\"pilot/settings\").settings;\nvar canon = require(\"pilot/canon\");\nrequire(\"ace/commands/default_commands\");\n\nvar KeyBinding = function(editor) {\n    this.$editor = editor;\n    this.$data = { };\n    this.$keyboardHandler = null;\n};\n\n(function() {\n    this.setKeyboardHandler = function(keyboardHandler) {\n        if (this.$keyboardHandler != keyboardHandler) {\n            this.$data = { };\n            this.$keyboardHandler = keyboardHandler;\n        }\n    };\n\n    this.getKeyboardHandler = function() {\n        return this.$keyboardHandler;\n    };\n\n    this.$callKeyboardHandler = function (e, hashId, keyOrText, keyCode) {\n        var env = {editor: this.$editor},\n            toExecute;\n\n        if (this.$keyboardHandler) {\n            toExecute =\n                this.$keyboardHandler.handleKeyboard(this.$data, hashId, keyOrText, keyCode, e);\n        }\n\n        // If there is nothing to execute yet, then use the default keymapping.\n        if (!toExecute || !toExecute.command) {\n            if (hashId != 0 || keyCode != 0) {\n                toExecute = {\n                    command: canon.findKeyCommand(env, \"editor\", hashId, keyOrText)\n                }\n            } else {\n                toExecute = {\n                    command: \"inserttext\",\n                    args: {\n                        text: keyOrText\n                    }\n                }\n            }\n        }\n\n        if (toExecute) {\n            var success = canon.exec(toExecute.command,\n                                        env, \"editor\", toExecute.args);\n            if (success) {\n                return event.stopEvent(e);\n            }\n        }\n    };\n\n    this.onCommandKey = function(e, hashId, keyCode) {\n        var keyString = keyUtil.keyCodeToString(keyCode);\n        this.$callKeyboardHandler(e, hashId, keyString, keyCode);\n    };\n\n    this.onTextInput = function(text) {\n        this.$callKeyboardHandler({}, 0, text, 0);\n    }\n\n}).call(KeyBinding.prototype);\n\nexports.KeyBinding = KeyBinding;\n});\n/* vim:ts=4:sts=4:sw=4:\n * ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Ajax.org Code Editor (ACE).\n *\n * The Initial Developer of the Original Code is\n * Ajax.org B.V.\n * Portions created by the Initial Developer are Copyright (C) 2010\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *      Fabian Jakobs <fabian AT ajax DOT org>\n *      Julian Viereck <julian.viereck@gmail.com>\n *      Mihai Sucan <mihai.sucan@gmail.com>\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('ace/commands/default_commands', function(require, exports, module) {\n\nvar lang = require(\"pilot/lang\");\nvar canon = require(\"pilot/canon\");\n\nfunction bindKey(win, mac) {\n    return {\n        win: win,\n        mac: mac,\n        sender: \"editor\"\n    };\n}\n\ncanon.addCommand({\n    name: \"null\",\n    exec: function(env, args, request) {  }\n});\n\ncanon.addCommand({\n    name: \"selectall\",\n    bindKey: bindKey(\"Ctrl-A\", \"Command-A\"),\n    exec: function(env, args, request) { env.editor.selectAll(); }\n});\ncanon.addCommand({\n    name: \"removeline\",\n    bindKey: bindKey(\"Ctrl-D\", \"Command-D\"),\n    exec: function(env, args, request) { env.editor.removeLines(); }\n});\ncanon.addCommand({\n    name: \"gotoline\",\n    bindKey: bindKey(\"Ctrl-L\", \"Command-L\"),\n    exec: function(env, args, request) {\n        var line = parseInt(prompt(\"Enter line number:\"));\n        if (!isNaN(line)) {\n            env.editor.gotoLine(line);\n        }\n    }\n});\ncanon.addCommand({\n    name: \"togglecomment\",\n    bindKey: bindKey(\"Ctrl-7\", \"Command-7\"),\n    exec: function(env, args, request) { env.editor.toggleCommentLines(); }\n});\ncanon.addCommand({\n    name: \"findnext\",\n    bindKey: bindKey(\"Ctrl-K\", \"Command-G\"),\n    exec: function(env, args, request) { env.editor.findNext(); }\n});\ncanon.addCommand({\n    name: \"findprevious\",\n    bindKey: bindKey(\"Ctrl-Shift-K\", \"Command-Shift-G\"),\n    exec: function(env, args, request) { env.editor.findPrevious(); }\n});\ncanon.addCommand({\n    name: \"find\",\n    bindKey: bindKey(\"Ctrl-F\", \"Command-F\"),\n    exec: function(env, args, request) {\n        var needle = prompt(\"Find:\");\n        env.editor.find(needle);\n    }\n});\ncanon.addCommand({\n    name: \"replace\",\n    bindKey: bindKey(\"Ctrl-R\", \"Command-Option-F\"),\n    exec: function(env, args, request) {\n        var needle = prompt(\"Find:\");\n        if (!needle)\n            return;\n        var replacement = prompt(\"Replacement:\");\n        if (!replacement)\n            return;\n        env.editor.replace(replacement, {needle: needle});\n    }\n});\ncanon.addCommand({\n    name: \"replaceall\",\n    bindKey: bindKey(\"Ctrl-Shift-R\", \"Command-Shift-Option-F\"),\n    exec: function(env, args, request) {\n        var needle = prompt(\"Find:\");\n        if (!needle)\n            return;\n        var replacement = prompt(\"Replacement:\");\n        if (!replacement)\n            return;\n        env.editor.replaceAll(replacement, {needle: needle});\n    }\n});\ncanon.addCommand({\n    name: \"undo\",\n    bindKey: bindKey(\"Ctrl-Z\", \"Command-Z\"),\n    exec: function(env, args, request) { env.editor.undo(); }\n});\ncanon.addCommand({\n    name: \"redo\",\n    bindKey: bindKey(\"Ctrl-Shift-Z|Ctrl-Y\", \"Command-Shift-Z|Command-Y\"),\n    exec: function(env, args, request) { env.editor.redo(); }\n});\ncanon.addCommand({\n    name: \"overwrite\",\n    bindKey: bindKey(\"Insert\", \"Insert\"),\n    exec: function(env, args, request) { env.editor.toggleOverwrite(); }\n});\ncanon.addCommand({\n    name: \"copylinesup\",\n    bindKey: bindKey(\"Ctrl-Alt-Up\", \"Command-Option-Up\"),\n    exec: function(env, args, request) { env.editor.copyLinesUp(); }\n});\ncanon.addCommand({\n    name: \"movelinesup\",\n    bindKey: bindKey(\"Alt-Up\", \"Option-Up\"),\n    exec: function(env, args, request) { env.editor.moveLinesUp(); }\n});\ncanon.addCommand({\n    name: \"selecttostart\",\n    bindKey: bindKey(\"Alt-Shift-Up\", \"Command-Shift-Up\"),\n    exec: function(env, args, request) { env.editor.getSelection().selectFileStart(); }\n});\ncanon.addCommand({\n    name: \"gotostart\",\n    bindKey: bindKey(\"Ctrl-Home|Ctrl-Up\", \"Command-Home|Command-Up\"),\n    exec: function(env, args, request) { env.editor.navigateFileStart(); }\n});\ncanon.addCommand({\n    name: \"selectup\",\n    bindKey: bindKey(\"Shift-Up\", \"Shift-Up\"),\n    exec: function(env, args, request) { env.editor.getSelection().selectUp(); }\n});\ncanon.addCommand({\n    name: \"golineup\",\n    bindKey: bindKey(\"Up\", \"Up|Ctrl-P\"),\n    exec: function(env, args, request) { env.editor.navigateUp(args.times); }\n});\ncanon.addCommand({\n    name: \"copylinesdown\",\n    bindKey: bindKey(\"Ctrl-Alt-Down\", \"Command-Option-Down\"),\n    exec: function(env, args, request) { env.editor.copyLinesDown(); }\n});\ncanon.addCommand({\n    name: \"movelinesdown\",\n    bindKey: bindKey(\"Alt-Down\", \"Option-Down\"),\n    exec: function(env, args, request) { env.editor.moveLinesDown(); }\n});\ncanon.addCommand({\n    name: \"selecttoend\",\n    bindKey: bindKey(\"Alt-Shift-Down\", \"Command-Shift-Down\"),\n    exec: function(env, args, request) { env.editor.getSelection().selectFileEnd(); }\n});\ncanon.addCommand({\n    name: \"gotoend\",\n    bindKey: bindKey(\"Ctrl-End|Ctrl-Down\", \"Command-End|Command-Down\"),\n    exec: function(env, args, request) { env.editor.navigateFileEnd(); }\n});\ncanon.addCommand({\n    name: \"selectdown\",\n    bindKey: bindKey(\"Shift-Down\", \"Shift-Down\"),\n    exec: function(env, args, request) { env.editor.getSelection().selectDown(); }\n});\ncanon.addCommand({\n    name: \"golinedown\",\n    bindKey: bindKey(\"Down\", \"Down|Ctrl-N\"),\n    exec: function(env, args, request) { env.editor.navigateDown(args.times); }\n});\ncanon.addCommand({\n    name: \"selectwordleft\",\n    bindKey: bindKey(\"Ctrl-Shift-Left\", \"Option-Shift-Left\"),\n    exec: function(env, args, request) { env.editor.getSelection().selectWordLeft(); }\n});\ncanon.addCommand({\n    name: \"gotowordleft\",\n    bindKey: bindKey(\"Ctrl-Left\", \"Option-Left\"),\n    exec: function(env, args, request) { env.editor.navigateWordLeft(); }\n});\ncanon.addCommand({\n    name: \"selecttolinestart\",\n    bindKey: bindKey(\"Alt-Shift-Left\", \"Command-Shift-Left\"),\n    exec: function(env, args, request) { env.editor.getSelection().selectLineStart(); }\n});\ncanon.addCommand({\n    name: \"gotolinestart\",\n    bindKey: bindKey(\"Alt-Left|Home\", \"Command-Left|Home|Ctrl-A\"),\n    exec: function(env, args, request) { env.editor.navigateLineStart(); }\n});\ncanon.addCommand({\n    name: \"selectleft\",\n    bindKey: bindKey(\"Shift-Left\", \"Shift-Left\"),\n    exec: function(env, args, request) { env.editor.getSelection().selectLeft(); }\n});\ncanon.addCommand({\n    name: \"gotoleft\",\n    bindKey: bindKey(\"Left\", \"Left|Ctrl-B\"),\n    exec: function(env, args, request) { env.editor.navigateLeft(args.times); }\n});\ncanon.addCommand({\n    name: \"selectwordright\",\n    bindKey: bindKey(\"Ctrl-Shift-Right\", \"Option-Shift-Right\"),\n    exec: function(env, args, request) { env.editor.getSelection().selectWordRight(); }\n});\ncanon.addCommand({\n    name: \"gotowordright\",\n    bindKey: bindKey(\"Ctrl-Right\", \"Option-Right\"),\n    exec: function(env, args, request) { env.editor.navigateWordRight(); }\n});\ncanon.addCommand({\n    name: \"selecttolineend\",\n    bindKey: bindKey(\"Alt-Shift-Right\", \"Command-Shift-Right\"),\n    exec: function(env, args, request) { env.editor.getSelection().selectLineEnd(); }\n});\ncanon.addCommand({\n    name: \"gotolineend\",\n    bindKey: bindKey(\"Alt-Right|End\", \"Command-Right|End|Ctrl-E\"),\n    exec: function(env, args, request) { env.editor.navigateLineEnd(); }\n});\ncanon.addCommand({\n    name: \"selectright\",\n    bindKey: bindKey(\"Shift-Right\", \"Shift-Right\"),\n    exec: function(env, args, request) { env.editor.getSelection().selectRight(); }\n});\ncanon.addCommand({\n    name: \"gotoright\",\n    bindKey: bindKey(\"Right\", \"Right|Ctrl-F\"),\n    exec: function(env, args, request) { env.editor.navigateRight(args.times); }\n});\ncanon.addCommand({\n    name: \"selectpagedown\",\n    bindKey: bindKey(\"Shift-PageDown\", \"Shift-PageDown\"),\n    exec: function(env, args, request) { env.editor.selectPageDown(); }\n});\ncanon.addCommand({\n    name: \"pagedown\",\n    bindKey: bindKey(null, \"PageDown\"),\n    exec: function(env, args, request) { env.editor.scrollPageDown(); }\n});\ncanon.addCommand({\n    name: \"gotopagedown\",\n    bindKey: bindKey(\"PageDown\", \"Option-PageDown|Ctrl-V\"),\n    exec: function(env, args, request) { env.editor.gotoPageDown(); }\n});\ncanon.addCommand({\n    name: \"selectpageup\",\n    bindKey: bindKey(\"Shift-PageUp\", \"Shift-PageUp\"),\n    exec: function(env, args, request) { env.editor.selectPageUp(); }\n});\ncanon.addCommand({\n    name: \"pageup\",\n    bindKey: bindKey(null, \"PageUp\"),\n    exec: function(env, args, request) { env.editor.scrollPageUp(); }\n});\ncanon.addCommand({\n    name: \"gotopageup\",\n    bindKey: bindKey(\"PageUp\", \"Option-PageUp\"),\n    exec: function(env, args, request) { env.editor.gotoPageUp(); }\n});\ncanon.addCommand({\n    name: \"selectlinestart\",\n    bindKey: bindKey(\"Shift-Home\", \"Shift-Home\"),\n    exec: function(env, args, request) { env.editor.getSelection().selectLineStart(); }\n});\ncanon.addCommand({\n    name: \"selectlineend\",\n    bindKey: bindKey(\"Shift-End\", \"Shift-End\"),\n    exec: function(env, args, request) { env.editor.getSelection().selectLineEnd(); }\n});\ncanon.addCommand({\n    name: \"del\",\n    bindKey: bindKey(\"Delete\", \"Delete|Ctrl-D\"),\n    exec: function(env, args, request) { env.editor.removeRight(); }\n});\ncanon.addCommand({\n    name: \"backspace\",\n    bindKey: bindKey(\n        \"Ctrl-Backspace|Command-Backspace|Option-Backspace|Shift-Backspace|Backspace\",\n        \"Ctrl-Backspace|Command-Backspace|Shift-Backspace|Backspace|Ctrl-H\"\n    ),\n    exec: function(env, args, request) { env.editor.removeLeft(); }\n});\ncanon.addCommand({\n    name: \"removetolinestart\",\n    bindKey: bindKey(null, \"Option-Backspace\"),\n    exec: function(env, args, request) { env.editor.removeToLineStart(); }\n});\ncanon.addCommand({\n    name: \"removetolineend\",\n    bindKey: bindKey(null, \"Ctrl-K\"),\n    exec: function(env, args, request) { env.editor.removeToLineEnd(); }\n});\ncanon.addCommand({\n    name: \"removewordleft\",\n    bindKey: bindKey(null, \"Alt-Backspace|Ctrl-Alt-Backspace\"),\n    exec: function(env, args, request) { env.editor.removeWordLeft(); }\n});\ncanon.addCommand({\n    name: \"removewordright\",\n    bindKey: bindKey(null, \"Alt-Delete\"),\n    exec: function(env, args, request) { env.editor.removeWordRight(); }\n});\ncanon.addCommand({\n    name: \"outdent\",\n    bindKey: bindKey(\"Shift-Tab\", \"Shift-Tab\"),\n    exec: function(env, args, request) { env.editor.blockOutdent(); }\n});\ncanon.addCommand({\n    name: \"indent\",\n    bindKey: bindKey(\"Tab\", \"Tab\"),\n    exec: function(env, args, request) { env.editor.indent(); }\n});\ncanon.addCommand({\n    name: \"inserttext\",\n    exec: function(env, args, request) {\n        env.editor.insert(lang.stringRepeat(args.text  || \"\", args.times || 1));\n    }\n});\ncanon.addCommand({\n    name: \"splitline\",\n    bindKey: bindKey(null, \"Ctrl-O\"),\n    exec: function(env, args, request) { env.editor.splitLine(); }\n});\ncanon.addCommand({\n    name: \"transposeletters\",\n    bindKey: bindKey(\"Ctrl-T\", \"Ctrl-T\"),\n    exec: function(env, args, request) { env.editor.transposeLetters(); }\n});\n\n});/* vim:ts=4:sts=4:sw=4:\n * ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Ajax.org Code Editor (ACE).\n *\n * The Initial Developer of the Original Code is\n * Ajax.org B.V.\n * Portions created by the Initial Developer are Copyright (C) 2010\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *      Fabian Jakobs <fabian AT ajax DOT org>\n *      Mihai Sucan <mihai DOT sucan AT gmail DOT com>\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('ace/edit_session', function(require, exports, module) {\n\nvar oop = require(\"pilot/oop\");\nvar lang = require(\"pilot/lang\");\nvar EventEmitter = require(\"pilot/event_emitter\").EventEmitter;\nvar Selection = require(\"ace/selection\").Selection;\nvar TextMode = require(\"ace/mode/text\").Mode;\nvar Range = require(\"ace/range\").Range;\nvar Document = require(\"ace/document\").Document;\n\nvar EditSession = function(text, mode) {\n    this.$modified = true;\n    this.$breakpoints = [];\n    this.$frontMarkers = {};\n    this.$backMarkers = {};\n    this.$markerId = 1;\n    this.$wrapData = [];\n\n    if (text instanceof Document) {\n        this.setDocument(text);\n    } else {\n        this.setDocument(new Document(text));\n    }\n\n    this.selection = new Selection(this);\n    if (mode)\n        this.setMode(mode);\n};\n\n\n(function() {\n\n    oop.implement(this, EventEmitter);\n\n    this.setDocument = function(doc) {\n        if (this.doc)\n            throw new Error(\"Document is already set\");\n\n        this.doc = doc;\n        doc.on(\"change\", this.onChange.bind(this));\n    };\n\n    this.getDocument = function() {\n        return this.doc;\n    };\n\n    this.onChange = function(e) {\n        var delta = e.data;\n        this.$modified = true;\n        if (!this.$fromUndo && this.$undoManager && !delta.ignore) {\n            this.$deltas.push(delta);\n            this.$informUndoManager.schedule();\n        }\n\n        this.$updateWrapDataOnChange(e);\n        this._dispatchEvent(\"change\", e);\n    };\n\n    this.setValue = function(text) {\n        this.doc.setValue(text);\n        this.$deltas = [];\n        this.getUndoManager().reset();\n    };\n\n    this.getValue =\n    this.toString = function() {\n        return this.doc.getValue();\n    };\n\n    this.getSelection = function() {\n        return this.selection;\n    };\n\n    this.setUndoManager = function(undoManager) {\n        this.$undoManager = undoManager;\n        this.$deltas = [];\n\n        if (this.$informUndoManager) {\n            this.$informUndoManager.cancel();\n        }\n\n        if (undoManager) {\n            var self = this;\n            this.$informUndoManager = lang.deferredCall(function() {\n                if (self.$deltas.length > 0)\n                    undoManager.execute({\n                        action : \"aceupdate\",\n                        args   : [self.$deltas, self]\n                    });\n                self.$deltas = [];\n            });\n        }\n    };\n\n    this.$defaultUndoManager = {\n        undo: function() {},\n        redo: function() {},\n        reset: function() {}\n    };\n\n    this.getUndoManager = function() {\n        return this.$undoManager || this.$defaultUndoManager;\n    },\n\n    this.getTabString = function() {\n        if (this.getUseSoftTabs()) {\n            return lang.stringRepeat(\" \", this.getTabSize());\n        } else {\n            return \"\\t\";\n        }\n    };\n\n    this.$useSoftTabs = true;\n    this.setUseSoftTabs = function(useSoftTabs) {\n        if (this.$useSoftTabs === useSoftTabs) return;\n\n        this.$useSoftTabs = useSoftTabs;\n    };\n\n    this.getUseSoftTabs = function() {\n        return this.$useSoftTabs;\n    };\n\n    this.$tabSize = 4;\n    this.setTabSize = function(tabSize) {\n        if (isNaN(tabSize) || this.$tabSize === tabSize) return;\n\n        this.$modified = true;\n        this.$tabSize = tabSize;\n        this._dispatchEvent(\"changeTabSize\");\n    };\n\n    this.getTabSize = function() {\n        return this.$tabSize;\n    };\n\n    this.isTabStop = function(position) {\n        return this.$useSoftTabs && (position.column % this.$tabSize == 0);\n    };\n\n    this.$overwrite = false;\n    this.setOverwrite = function(overwrite) {\n        if (this.$overwrite == overwrite) return;\n\n        this.$overwrite = overwrite;\n        this._dispatchEvent(\"changeOverwrite\");\n    };\n\n    this.getOverwrite = function() {\n        return this.$overwrite;\n    };\n\n    this.toggleOverwrite = function() {\n        this.setOverwrite(!this.$overwrite);\n    };\n\n    this.getBreakpoints = function() {\n        return this.$breakpoints;\n    };\n\n    this.setBreakpoints = function(rows) {\n        this.$breakpoints = [];\n        for (var i=0; i<rows.length; i++) {\n            this.$breakpoints[rows[i]] = true;\n        }\n        this._dispatchEvent(\"changeBreakpoint\", {});\n    };\n\n    this.clearBreakpoints = function() {\n        this.$breakpoints = [];\n        this._dispatchEvent(\"changeBreakpoint\", {});\n    };\n\n    this.setBreakpoint = function(row) {\n        this.$breakpoints[row] = true;\n        this._dispatchEvent(\"changeBreakpoint\", {});\n    };\n\n    this.clearBreakpoint = function(row) {\n        delete this.$breakpoints[row];\n        this._dispatchEvent(\"changeBreakpoint\", {});\n    };\n\n    this.getBreakpoints = function() {\n        return this.$breakpoints;\n    };\n\n    this.addMarker = function(range, clazz, type, inFront) {\n        var id = this.$markerId++;\n\n        var marker = {\n            range : range,\n            type : type || \"line\",\n            renderer: typeof type == \"function\" ? type : null,\n            clazz : clazz,\n            inFront: !!inFront\n        }\n\n        if (inFront) {\n            this.$frontMarkers[id] = marker;\n            this._dispatchEvent(\"changeFrontMarker\")\n        } else {\n            this.$backMarkers[id] = marker;\n            this._dispatchEvent(\"changeBackMarker\")\n        }\n\n        return id;\n    };\n\n    this.removeMarker = function(markerId) {\n        var marker = this.$frontMarkers[markerId] || this.$backMarkers[markerId];\n        if (!marker)\n            return;\n\n        var markers = marker.inFront ? this.$frontMarkers : this.$backMarkers;\n        if (marker) {\n            delete (markers[markerId]);\n            this._dispatchEvent(marker.inFront ? \"changeFrontMarker\" : \"changeBackMarker\");\n        }\n    };\n\n    this.getMarkers = function(inFront) {\n        return inFront ? this.$frontMarkers : this.$backMarkers;\n    };\n\n    /**\n     * Error:\n     *  {\n     *    row: 12,\n     *    column: 2, //can be undefined\n     *    text: \"Missing argument\",\n     *    type: \"error\" // or \"warning\" or \"info\"\n     *  }\n     */\n    this.setAnnotations = function(annotations) {\n        this.$annotations = {};\n        for (var i=0; i<annotations.length; i++) {\n            var annotation = annotations[i];\n            var row = annotation.row;\n            if (this.$annotations[row])\n                this.$annotations[row].push(annotation);\n            else\n                this.$annotations[row] = [annotation];\n        }\n        this._dispatchEvent(\"changeAnnotation\", {});\n    };\n\n    this.getAnnotations = function() {\n        return this.$annotations;\n    };\n\n    this.clearAnnotations = function() {\n        this.$annotations = {};\n        this._dispatchEvent(\"changeAnnotation\", {});\n    };\n\n    this.$detectNewLine = function(text) {\n        var match = text.match(/^.*?(\\r?\\n)/m);\n        if (match) {\n            this.$autoNewLine = match[1];\n        } else {\n            this.$autoNewLine = \"\\n\";\n        }\n    };\n\n    this.tokenRe = /^[\\w\\d]+/g;\n    this.nonTokenRe = /^(?:[^\\w\\d]|[\\u3040-\\u309F]|[\\u30A0-\\u30FF]|[\\u4E00-\\u9FFF\\uF900-\\uFAFF\\u3400-\\u4DBF])+/g;\n\n    this.getWordRange = function(row, column) {\n        var line = this.getLine(row);\n\n        var inToken = false;\n        if (column > 0) {\n            inToken = !!line.charAt(column - 1).match(this.tokenRe);\n        }\n\n        if (!inToken) {\n            inToken = !!line.charAt(column).match(this.tokenRe);\n        }\n\n        var re = inToken ? this.tokenRe : this.nonTokenRe;\n\n        var start = column;\n        if (start > 0) {\n            do {\n                start--;\n            }\n            while (start >= 0 && line.charAt(start).match(re));\n            start++;\n        }\n\n        var end = column;\n        while (end < line.length && line.charAt(end).match(re)) {\n            end++;\n        }\n\n        return new Range(row, start, row, end);\n    };\n\n    this.setNewLineMode = function(newLineMode) {\n        this.doc.setNewLineMode(newLineMode);\n    };\n\n    this.getNewLineMode = function() {\n        return this.doc.getNewLineMode();\n    };\n\n    this.$useWorker = true;\n    this.setUseWorker = function(useWorker) {\n        if (this.$useWorker == useWorker)\n            return;\n            \n        if (useWorker && !this.$worker && window.Worker)\n            this.$worker = mode.createWorker(this);\n            \n        if (!useWorker && this.$worker) {\n            this.$worker.terminate();\n            this.$worker = null;\n        }\n    };\n    \n    this.getUseWorker = function() {\n        return this.$useWorker;\n    };\n\n    this.$mode = null;\n    this.setMode = function(mode) {\n        if (this.$mode === mode) return;\n\n        if (this.$worker)\n            this.$worker.terminate();\n\n        if (this.$useWorker && window.Worker && !require.noWorker)\n            this.$worker = mode.createWorker(this);\n        else\n            this.$worker = null;\n\n        this.$mode = mode;\n        this._dispatchEvent(\"changeMode\");\n    };\n\n    this.getMode = function() {\n        if (!this.$mode) {\n            this.$mode = new TextMode();\n        }\n        return this.$mode;\n    };\n\n    this.$scrollTop = 0;\n    this.setScrollTopRow = function(scrollTopRow) {\n        if (this.$scrollTop === scrollTopRow) return;\n\n        this.$scrollTop = scrollTopRow;\n        this._dispatchEvent(\"changeScrollTop\");\n    };\n\n    this.getScrollTopRow = function() {\n        return this.$scrollTop;\n    };\n\n    this.getWidth = function() {\n        this.$computeWidth();\n        return this.width;\n    };\n\n    this.getScreenWidth = function() {\n        this.$computeWidth();\n        return this.screenWidth;\n    };\n\n    this.$computeWidth = function(force) {\n        if (this.$modified || force) {\n            this.$modified = false;\n\n            var lines = this.doc.getAllLines();\n            var longestLine = 0;\n            var longestScreenLine = 0;\n            var tabSize = this.getTabSize();\n\n            for ( var i = 0; i < lines.length; i++) {\n                var len = lines[i].length;\n                longestLine = Math.max(longestLine, len);\n\n                lines[i].replace(/\\t/g, function(m) {\n                    len += tabSize-1;\n                    return m;\n                });\n                longestScreenLine = Math.max(longestScreenLine, len);\n            }\n            this.width = longestLine;\n\n            if (this.$useWrapMode) {\n                this.screenWidth = this.$wrapLimit;\n            } else {\n                this.screenWidth = longestScreenLine;\n            }\n        }\n    };\n\n    /**\n     * Get a verbatim copy of the given line as it is in the document\n     */\n    this.getLine = function(row) {\n        return this.doc.getLine(row);\n    };\n\n    /**\n     * Get a line as it is displayed on screen. Tabs are replaced by spaces.\n     */\n    this.getDisplayLine = function(row) {\n        var tab = new Array(this.getTabSize()+1).join(\" \");\n        return this.doc.getLine(row).replace(/\\t/g, tab);\n    };\n\n    this.getLines = function(firstRow, lastRow) {\n        return this.doc.getLines(firstRow, lastRow);\n    };\n\n    this.getLength = function() {\n        return this.doc.getLength();\n    };\n\n    this.getTextRange = function(range) {\n        return this.doc.getTextRange(range);\n    };\n\n    this.findMatchingBracket = function(position) {\n        if (position.column == 0) return null;\n\n        var charBeforeCursor = this.getLine(position.row).charAt(position.column-1);\n        if (charBeforeCursor == \"\") return null;\n\n        var match = charBeforeCursor.match(/([\\(\\[\\{])|([\\)\\]\\}])/);\n        if (!match) {\n            return null;\n        }\n\n        if (match[1]) {\n            return this.$findClosingBracket(match[1], position);\n        } else {\n            return this.$findOpeningBracket(match[2], position);\n        }\n    };\n\n    this.$brackets = {\n        \")\": \"(\",\n        \"(\": \")\",\n        \"]\": \"[\",\n        \"[\": \"]\",\n        \"{\": \"}\",\n        \"}\": \"{\"\n    };\n\n    this.$findOpeningBracket = function(bracket, position) {\n        var openBracket = this.$brackets[bracket];\n\n        var column = position.column - 2;\n        var row = position.row;\n        var depth = 1;\n\n        var line = this.getLine(row);\n\n        while (true) {\n            while(column >= 0) {\n                var ch = line.charAt(column);\n                if (ch == openBracket) {\n                    depth -= 1;\n                    if (depth == 0) {\n                        return {row: row, column: column};\n                    }\n                }\n                else if (ch == bracket) {\n                    depth +=1;\n                }\n                column -= 1;\n            }\n            row -=1;\n            if (row < 0) break;\n\n            var line = this.getLine(row);\n            var column = line.length-1;\n        }\n        return null;\n    };\n\n    this.$findClosingBracket = function(bracket, position) {\n        var closingBracket = this.$brackets[bracket];\n\n        var column = position.column;\n        var row = position.row;\n        var depth = 1;\n\n        var line = this.getLine(row);\n        var lineCount = this.getLength();\n\n        while (true) {\n            while(column < line.length) {\n                var ch = line.charAt(column);\n                if (ch == closingBracket) {\n                    depth -= 1;\n                    if (depth == 0) {\n                        return {row: row, column: column};\n                    }\n                }\n                else if (ch == bracket) {\n                    depth +=1;\n                }\n                column += 1;\n            }\n            row +=1;\n            if (row >= lineCount) break;\n\n            var line = this.getLine(row);\n            var column = 0;\n        }\n        return null;\n    };\n\n    this.insert = function(position, text) {\n        return this.doc.insert(position, text);\n    };\n\n    this.remove = function(range) {\n        return this.doc.remove(range);\n    };\n\n    this.undoChanges = function(deltas) {\n        if (!deltas.length)\n            return;\n\n        this.$fromUndo = true;\n        this.doc.revertDeltas(deltas);\n        this.$fromUndo = false;\n\n        this.$setUndoSelection(deltas, true);\n    },\n\n    this.redoChanges = function(deltas) {\n        if (!deltas.length)\n            return;\n\n        this.$fromUndo = true;\n        this.doc.applyDeltas(deltas);\n        this.$fromUndo = false;\n\n        this.$setUndoSelection(deltas, false);\n    },\n\n    this.$setUndoSelection = function(deltas, isUndo) {\n        // invert deltas is they are an undo\n        if (isUndo)\n            deltas = deltas.map(function(delta) {\n                var d = {\n                    range: delta.range\n                }\n                if (delta.action == \"insertText\" || delta.action == \"insertLines\")\n                    d.action = \"removeText\"\n                else\n                    d.action = \"insertText\"\n                return d;\n            }).reverse();\n\n\n        var actions = [{}];\n        \n        // collapse insert and remove operations\n        for (var i=0; i<deltas.length; i++) {\n            var delta = deltas[i];\n            var isInsert = delta.action == \"insertText\" || delta.action == \"insertLines\";\n            var action = actions[actions.length-1];\n            if (action.isInsert !== isInsert) {\n                actions.push({\n                    isInsert: isInsert,\n                    start: isInsert ? delta.range.start : delta.range.end,\n                    end: isInsert ? delta.range.end : delta.range.start\n                })\n            }\n            else {\n                if (isInsert)    \n                    action.end = delta.range.end;\n                else\n                    action.start = delta.range.start;\n            }\n        }\n\n        // update selection based on last operation\n        this.selection.clearSelection();\n        var action = actions[actions.length-1];\n        if (action.isInsert) \n            this.selection.setSelectionRange(Range.fromPoints(action.start, action.end));\n        else \n            this.selection.moveCursorToPosition(action.end);\n    },\n    \n    this.replace = function(range, text) {\n        return this.doc.replace(range, text);\n    };\n\n    /**\n     * Move a range of text from the given range to the given position.\n     *\n     * @param fromRange {Range} The range of text you want moved within the\n     * document.\n     * @param toPosition {Object} The location (row and column) where you want\n     * to move the text to.\n     * @return {Range} The new range where the text was moved to.\n     */\n    this.moveText = function(fromRange, toPosition) {\n        var text = this.getTextRange(fromRange);\n        this.remove(fromRange);\n\n        var toRow = toPosition.row;\n        var toColumn = toPosition.column;\n\n        // Make sure to update the insert location, when text is removed in\n        // front of the chosen point of insertion.\n        if (!fromRange.isMultiLine() && fromRange.start.row == toRow &&\n            fromRange.end.column < toColumn)\n            toColumn -= text.length;\n\n        if (fromRange.isMultiLine() && fromRange.end.row < toRow) {\n            var lines = this.doc.$split(text);\n            toRow -= lines.length - 1;\n        }\n\n        var endRow = toRow + fromRange.end.row - fromRange.start.row;\n        var endColumn = fromRange.isMultiLine() ?\n                        fromRange.end.column :\n                        toColumn + fromRange.end.column - fromRange.start.column;\n\n        var toRange = new Range(toRow, toColumn, endRow, endColumn);\n\n        this.insert(toRange.start, text);\n\n        return toRange;\n    };\n\n    this.indentRows = function(startRow, endRow, indentString) {\n        indentString = indentString.replace(/\\t/g, this.getTabString());\n        for (var row=startRow; row<=endRow; row++) {\n            this.insert({row: row, column:0}, indentString);\n        }\n    };\n\n    this.outdentRows = function (range) {\n        var rowRange = range.collapseRows();\n        var deleteRange = new Range(0, 0, 0, 0);\n        var size = this.getTabSize();\n\n        for (var i = rowRange.start.row; i <= rowRange.end.row; ++i) {\n            var line = this.getLine(i);\n\n            deleteRange.start.row = i;\n            deleteRange.end.row = i;\n            for (var j = 0; j < size; ++j)\n                if (line.charAt(j) != ' ')\n                    break;\n            if (j < size && line.charAt(j) == '\\t') {\n                deleteRange.start.column = j;\n                deleteRange.end.column = j + 1;\n            } else {\n                deleteRange.start.column = 0;\n                deleteRange.end.column = j;\n            }\n            this.remove(deleteRange);\n        }\n    };\n\n    this.moveLinesUp = function(firstRow, lastRow) {\n        if (firstRow <= 0) return 0;\n\n        var removed = this.doc.removeLines(firstRow, lastRow);\n        this.doc.insertLines(firstRow - 1, removed);\n        return -1;\n    };\n\n    this.moveLinesDown = function(firstRow, lastRow) {\n        if (lastRow >= this.doc.getLength()-1) return 0;\n\n        var removed = this.doc.removeLines(firstRow, lastRow);\n        this.doc.insertLines(firstRow+1, removed);\n        return 1;\n    };\n\n    this.duplicateLines = function(firstRow, lastRow) {\n        var firstRow = this.$clipRowToDocument(firstRow);\n        var lastRow = this.$clipRowToDocument(lastRow);\n\n        var lines = this.getLines(firstRow, lastRow);\n        this.doc.insertLines(firstRow, lines);\n\n        var addedRows = lastRow - firstRow + 1;\n        return addedRows;\n    };\n\n    this.$clipRowToDocument = function(row) {\n        return Math.max(0, Math.min(row, this.doc.getLength()-1));\n    };\n\n    // WRAPMODE\n    this.$wrapLimit = 80;\n    this.$useWrapMode = false;\n    this.$wrapLimitRange = {\n        min : null,\n        max : null\n    };\n\n    this.setUseWrapMode = function(useWrapMode) {\n        if (useWrapMode != this.$useWrapMode) {\n            this.$useWrapMode = useWrapMode;\n            this.$modified = true;\n\n            // If wrapMode is activaed, the wrapData array has to be initialized.\n            if (useWrapMode) {\n                var len = this.getLength();\n                this.$wrapData = [];\n                for (i = 0; i < len; i++) {\n                    this.$wrapData.push([]);\n                }\n                this.$updateWrapData(0, len - 1);\n            }\n\n            this._dispatchEvent(\"changeWrapMode\");\n        }\n    };\n\n    this.getUseWrapMode = function() {\n        return this.$useWrapMode;\n    };\n\n    // Allow the wrap limit to move freely between min and max. Either\n    // parameter can be null to allow the wrap limit to be unconstrained\n    // in that direction. Or set both parameters to the same number to pin\n    // the limit to that value.\n    this.setWrapLimitRange = function(min, max) {\n        if (this.$wrapLimitRange.min !== min || this.$wrapLimitRange.max !== max) {\n            this.$wrapLimitRange.min = min;\n            this.$wrapLimitRange.max = max;\n            this.$modified = true;\n            // This will force a recalculation of the wrap limit\n            this._dispatchEvent(\"changeWrapMode\");\n        }\n    };\n\n    // This should generally only be called by the renderer when a resize\n    // is detected.\n    this.adjustWrapLimit = function(desiredLimit) {\n        var wrapLimit = this.$constrainWrapLimit(desiredLimit);\n        if (wrapLimit != this.$wrapLimit && wrapLimit > 0) {\n            this.$wrapLimit = wrapLimit;\n            this.$modified = true;\n            if (this.$useWrapMode) {\n                this.$updateWrapData(0, this.getLength() - 1);\n                this._dispatchEvent(\"changeWrapLimit\");\n            }\n            return true;\n        }\n        return false;\n    };\n\n    this.$constrainWrapLimit = function(wrapLimit) {\n        var min = this.$wrapLimitRange.min;\n        if (min)\n            wrapLimit = Math.max(min, wrapLimit);\n\n        var max = this.$wrapLimitRange.max;\n        if (max)\n            wrapLimit = Math.min(max, wrapLimit);\n\n        // What would a limit of 0 even mean?\n        return Math.max(1, wrapLimit);\n    };\n\n    this.getWrapLimit = function() {\n        return this.$wrapLimit;\n    };\n\n    this.getWrapLimitRange = function() {\n        // Avoid unexpected mutation by returning a copy\n        return {\n            min : this.$wrapLimitRange.min,\n            max : this.$wrapLimitRange.max\n        };\n    };\n\n    this.$updateWrapDataOnChange = function(e) {\n        if (!this.$useWrapMode) {\n            return;\n        }\n\n        var len;\n        var action = e.data.action;\n        var firstRow = e.data.range.start.row,\n            lastRow = e.data.range.end.row;\n\n        if (action.indexOf(\"Lines\") != -1) {\n            if (action == \"insertLines\") {\n                lastRow = firstRow + (e.data.lines.length);\n            } else {\n                lastRow = firstRow;\n            }\n            len = e.data.lines.length;\n        } else {\n            len = lastRow - firstRow;\n        }\n\n        if (len != 0) {\n            if (action.indexOf(\"remove\") != -1) {\n                this.$wrapData.splice(firstRow, len);\n                lastRow = firstRow;\n            } else {\n                var args = [firstRow, 0];\n                for (var i = 0; i < len; i++) args.push([]);\n                this.$wrapData.splice.apply(this.$wrapData, args);\n            }\n        }\n\n        if (this.$wrapData.length != this.doc.$lines.length) {\n            console.error(\"The length of doc.$lines and $wrapData have to be the same!\");\n        }\n\n        this.$updateWrapData(firstRow, lastRow);\n    };\n\n    this.$updateWrapData = function(firstRow, lastRow) {\n        var lines = this.doc.getAllLines();\n        var tabSize = this.getTabSize();\n        var wrapData = this.$wrapData;\n        var wrapLimit = this.$wrapLimit;\n\n        for (var row = firstRow; row <= lastRow; row++) {\n            wrapData[row] =\n                this.$computeWrapSplits(lines[row], wrapLimit, tabSize);\n        }\n    };\n\n    // \"Tokens\"\n    var CHAR = 1,\n        CHAR_EXT = 2,\n        SPACE = 3,\n        TAB = 4,\n        TAB_SPACE = 5;\n\n    this.$computeWrapSplits = function(textLine, wrapLimit, tabSize) {\n        textLine = textLine.trimRight();\n        if (textLine.length == 0) {\n            return [];\n        }\n\n        var tabSize = this.getTabSize();\n        var splits = [];\n        var tokens = this.$getDisplayTokens(textLine);\n        var displayLength = tokens.length;\n        var lastSplit = 0, lastDocSplit = 0;\n\n        function addSplit(screenPos) {\n            var displayed = tokens.slice(lastSplit, screenPos);\n\n            // The document size is the current size - the extra width for tabs\n            // and multipleWidth characters.\n            var len = displayed.length;\n            displayed.join(\"\").\n                // Get all the tabs.\n                replace(/4/g, function(m) {\n                    len -= tabSize - 1;\n                }).\n                // Get all the multipleWidth characters.\n                replace(/2/g, function(m) {\n                    len -= 1;\n                });\n\n            lastDocSplit += len;\n            splits.push(lastDocSplit);\n            lastSplit = screenPos;\n        }\n\n        while (displayLength - lastSplit > wrapLimit) {\n            // This is, where the split should be.\n            var split = lastSplit + wrapLimit;\n\n            // If there is a space or tab at this split position.\n            if (tokens[split] >= SPACE) {\n                // Include all following spaces + tabs in this split as well.\n                while (tokens[split] >= SPACE)  {\n                    split ++;\n                }\n                addSplit(split);\n            } else {\n                // Search for the first non space/tab token.\n                for (split; split != lastSplit - 1; split--) {\n                    if (tokens[split] >= SPACE) {\n                        split++;\n                        break;\n                    }\n                }\n                // If we found one, then add the split.\n                if (split > lastSplit) {\n                    addSplit(split);\n                }\n                // No space or tab around? Well, force a split then.\n                else {\n                    addSplit(lastSplit + wrapLimit);\n                }\n            }\n        }\n        return splits;\n    }\n\n    this.$getDisplayTokens = function(str) {\n        var arr = [];\n        var tabSize = this.getTabSize();\n\n        for (var i = 0; i < str.length; i++) {\n\t\t\tvar c = str.charCodeAt(i);\n\t\t\t// Tab\n\t\t\tif (c == 9) {\n\t\t\t    arr.push(TAB);\n\t\t\t    for (var n = 1; n < tabSize; n++) {\n\t\t\t        arr.push(TAB_SPACE);\n\t\t\t    }\n\t\t\t}\n\t\t\t// Space\n\t\t\telse if(c == 32) {\n\t\t\t    arr.push(SPACE);\n\t\t\t}\n    \t\t// CJK characters\n            else if (\n                c >= 0x3040 && c <= 0x309F || // Hiragana\n                c >= 0x30A0 && c <= 0x30FF || // Katakana\n                c >= 0x4E00 && c <= 0x9FFF || // Single CJK ideographs\n                c >= 0xF900 && c <= 0xFAFF ||\n                c >= 0x3400 && c <= 0x4DBF\n            ) {\n                arr.push(CHAR, CHAR_EXT);\n            } else {\n                arr.push(CHAR);\n            }\n        }\n        return arr;\n    }\n\n    /**\n     * Calculates the width of the a string on the screen while assuming that\n     * the string starts at the first column on the screen.\n     *\n     * @param string str String to calculate the screen width of\n     * @return int number of columns for str on screen.\n     */\n    this.$getStringScreenWidth = function(str) {\n        var screenColumn = 0;\n        var tabSize = this.getTabSize();\n\t\t\n\t\tfor (var i=0; i<str.length; i++) {\n\t\t\tvar c = str.charCodeAt(i);\n\t\t\t// tab\n\t\t\tif (c == 9) {\n\t\t\t\tscreenColumn += tabSize;\n\t\t\t}\n    \t\t// CJK characters\n            else if (\n                c >= 0x3040 && c <= 0x309F || // Hiragana\n                c >= 0x30A0 && c <= 0x30FF || // Katakana\n                c >= 0x4E00 && c <= 0x9FFF || // Single CJK ideographs\n                c >= 0xF900 && c <= 0xFAFF ||\n                c >= 0x3400 && c <= 0x4DBF\n            ) {\n            \tscreenColumn += 2;\n\t\t\t} else {\n\t\t\t\tscreenColumn += 1;\n\t\t\t}\n\t\t}\n\t\t\n\t\treturn screenColumn;\n    }\n\n    this.getRowHeight = function(config, row) {\n        var rows;\n        if (!this.$useWrapMode || !this.$wrapData[row]) {\n            rows = 1;\n        } else {\n            rows = this.$wrapData[row].length + 1;\n        }\n\n        return rows * config.lineHeight;\n    }\n\n    this.getScreenLastRowColumn = function(screenRow, returnDocPosition) {\n        if (!this.$useWrapMode) {\n            return this.$getStringScreenWidth(this.getLine(screenRow));\n        }\n\n        var rowData = this.$screenToDocumentRow(screenRow);\n        var docRow = rowData[0],\n            row = rowData[1];\n\n        var start, end;\n        if (this.$wrapData[docRow][row]) {\n            start = (this.$wrapData[docRow][row - 1] || 0);\n            end = this.$wrapData[docRow][row];\n            returnDocPosition && end--;\n        } else {\n            end = this.getLine(docRow).length;\n            start = (this.$wrapData[docRow][row - 1] || 0);\n        }\n        if (!returnDocPosition) {\n            return this.$getStringScreenWidth(this.getLine(docRow).substring(start, end));\n        } else {\n            return end;\n        }\n    };\n\n    this.getDocumentLastRowColumn = function(docRow, docColumn) {\n        if (!this.$useWrapMode) {\n            return this.getLine(docRow).length;\n        }\n\n        var screenRow = this.documentToScreenRow(docRow, docColumn);\n        return this.getScreenLastRowColumn(screenRow, true);\n    }\n\n    this.getScreenFirstRowColumn = function(screenRow) {\n        if (!this.$useWrapMode) {\n            return 0;\n        }\n\n        var rowData = this.$screenToDocumentRow(screenRow);\n        var docRow = rowData[0],\n            row = rowData[1];\n\n        return this.$wrapData[docRow][row - 1] || 0;\n    };\n\n    this.getRowSplitData = function(row) {\n        if (!this.$useWrapMode) {\n            return undefined;\n        } else {\n            return this.$wrapData[row];\n        }\n    };\n\n    /**\n     *\n     * @returns array\n     * - array[0]: The documentRow equivalent.\n     * - array[1]: The screenRowOffset to the first documentRow on the screen.\n     */\n    this.$screenToDocumentRow = function(row) {\n        if (!this.$useWrapMode) {\n            return [row, 0];\n        }\n\n        var wrapData = this.$wrapData, linesCount = this.getLength();\n        var docRow = 0;\n        while (docRow < linesCount && row >= wrapData[docRow].length + 1) {\n            row -= wrapData[docRow].length + 1;\n            docRow ++;\n        }\n\n        return [docRow, row];\n    };\n\n    this.screenToDocumentRow = function(screenRow) {\n        return this.$screenToDocumentRow(screenRow)[0];\n    };\n\n    this.screenToDocumentColumn = function(screenRow, screenColumn) {\n        return this.screenToDocumentPosition(screenRow, screenColumn).column;\n    };\n\n    this.screenToDocumentPosition = function(row, column) {\n        var line;\n        var docRow;\n        var docColumn;\n        var remaining = column;\n        var linesCount = this.getLength();\n        if (!this.$useWrapMode) {\n            docRow = row >= linesCount? linesCount-1 : (row < 0 ? 0 : row);\n            row = 0;\n            docColumn = 0;\n            line = this.getLine(docRow);\n        } else {\n            var wrapData = this.$wrapData;\n\n\t\t\tvar docRow = 0;\n\t\t\twhile (docRow < linesCount && row >= wrapData[docRow].length + 1) {\n\t\t\t\trow -= wrapData[docRow].length + 1;\n\t\t\t\tdocRow ++;\n\t\t\t}\n\n            if (docRow >= linesCount) {\n                docRow = linesCount-1\n\t\t\t\trow = wrapData[docRow].length;\n            }\n            docColumn = wrapData[docRow][row - 1] || 0;\n            line = this.getLine(docRow).substring(docColumn);\n        }\n\n        var tabSize = this.getTabSize();\n        for(var i = 0; i < line.length; i++) {\n            var c = line.charCodeAt(i);\n\n            if (remaining > 0) {\n                docColumn += 1;\n                // tab\n                if (c == 9) {\n                    if (remaining >= tabSize) {\n                        remaining -= tabSize;\n                    } else {\n                        remaining = 0;\n                        docColumn -= 1;\n                    }\n                }\n                // CJK characters\n                else if (\n                    c >= 0x3040 && c <= 0x309F || // Hiragana\n                    c >= 0x30A0 && c <= 0x30FF || // Katakana\n                    c >= 0x4E00 && c <= 0x9FFF || // Single CJK ideographs\n                    c >= 0xF900 && c <= 0xFAFF ||\n                    c >= 0x3400 && c <= 0x4DBF\n                ) {\n                    if (remaining >= 2) {\n                        remaining -= 2;\n                    } else {\n                        remaining = 0;\n                        docColumn -= 1;\n                    }\n                } else {\n                    remaining -= 1;\n                }\n            } else {\n                break;\n            }\n        }\n\n        // Clamp docColumn.\n        if (this.$useWrapMode) {\n            column = wrapData[docRow][row]\n\t\t\tif (docColumn >= column) {\n                // We remove one character at the end such that the docColumn\n                // position returned is not associated to the next row on the\n                // screen.\n                docColumn = column - 1;\n            }\n        } else if (line) {\n             docColumn = Math.min(docColumn, line.length);\n        }\n\n        return {\n            row: docRow,\n            column: docColumn\n        };\n    };\n\n    this.documentToScreenColumn = function(row, docColumn) {\n        return this.documentToScreenPosition(row, docColumn).column;\n    };\n\n    /**\n     *\n     * @return array[2]\n     * - array[0]: The number of the row on the screen (aka screenRow)\n     * - array[1]: The number of rows from the first docRow on the screen\n     *              (aka screenRowOffset);\n     */\n    this.$documentToScreenRow = function(docRow, docColumn) {\n        if (!this.$useWrapMode) {\n            return [docRow, 0];\n        }\n\n        var wrapData = this.$wrapData;\n        var screenRow = 0;\n\n        // Handle special case where the row is outside of the range of lines.\n        if (docRow > wrapData.length - 1) {\n            return [\n                this.getScreenLength(),\n                wrapData.length == 0 ? 0 : (wrapData[wrapData.length - 1].length - 1)\n            ];\n        }\n\n        for (var i = 0; i < docRow; i++) {\n            screenRow += wrapData[i].length + 1;\n        }\n\n        var screenRowOffset = 0;\n        while (docColumn >= wrapData[docRow][screenRowOffset]) {\n            screenRow ++;\n            screenRowOffset++;\n        }\n\n        return [screenRow, screenRowOffset];\n    }\n\n    this.documentToScreenRow = function(docRow, docColumn) {\n        return this.$documentToScreenRow(docRow, docColumn)[0];\n    }\n\n    this.documentToScreenPosition = function(pos, column) {\n        var str;\n        var tabSize = this.getTabSize();\n\n        // Normalize the passed in arguments.\n        var row;\n        if (column != null) {\n            row = pos;\n        } else {\n            row = pos.row;\n            column = pos.column;\n        }\n\n        if (!this.$useWrapMode) {\n            str = this.getLine(row).substring(0, column);\n            column = this.$getStringScreenWidth(str);\n            return {\n                row: row,\n                column: column\n            };\n        }\n\n        var rowData = this.$documentToScreenRow(row, column);\n        var screenRow = rowData[0];\n\n        if (row >= this.getLength()) {\n            return {\n                row: screenRow,\n                column: 0\n            };\n        }\n\n        var split;\n        var wrapRowData = this.$wrapData[row];\n        var screenColumn;\n        var screenRowOffset = rowData[1];\n\n        str = this.getLine(row).substring(\n                wrapRowData[screenRowOffset - 1] || 0,  column);\n        screenColumn = this.$getStringScreenWidth(str);\n\n        return {\n            row: screenRow,\n            column: screenColumn\n        };\n    };\n\n    this.getScreenLength = function() {\n        if (!this.$useWrapMode) {\n            return this.getLength();\n        }\n\n        var screenRows = 0;\n        for (var row = 0; row < this.$wrapData.length; row++) {\n            screenRows += this.$wrapData[row].length + 1;\n        }\n        return screenRows;\n    }\n\n}).call(EditSession.prototype);\n\nexports.EditSession = EditSession;\n});\n/* ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Ajax.org Code Editor (ACE).\n *\n * The Initial Developer of the Original Code is\n * Ajax.org B.V.\n * Portions created by the Initial Developer are Copyright (C) 2010\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *      Fabian Jakobs <fabian AT ajax DOT org>\n *      Julian Viereck <julian.viereck@gmail.com>\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('ace/selection', function(require, exports, module) {\n\nvar oop = require(\"pilot/oop\");\nvar lang = require(\"pilot/lang\");\nvar EventEmitter = require(\"pilot/event_emitter\").EventEmitter;\nvar Range = require(\"ace/range\").Range;\n\nvar Selection = function(session) {\n    this.session = session;\n    this.doc = session.getDocument();\n\n    this.clearSelection();\n    this.selectionLead = this.doc.createAnchor(0, 0);\n    this.selectionAnchor = this.doc.createAnchor(0, 0);\n    \n    var _self = this;\n    this.selectionLead.on(\"change\", function(e) {\n        _self._dispatchEvent(\"changeCursor\");\n        if (!_self.$isEmpty)\n            _self._dispatchEvent(\"changeSelection\");\n        if (e.old.row == e.value.row)\n            _self.$updateDesiredColumn();\n    });\n    \n    this.selectionAnchor.on(\"change\", function() {\n        if (!_self.$isEmpty)\n            _self._dispatchEvent(\"changeSelection\");\n    });\n};\n\n(function() {\n\n    oop.implement(this, EventEmitter);\n\n    this.isEmpty = function() {\n        return (this.$isEmpty || (\n            this.selectionAnchor.row == this.selectionLead.row &&\n            this.selectionAnchor.column == this.selectionLead.column\n        ));\n    };\n\n    this.isMultiLine = function() {\n        if (this.isEmpty()) {\n            return false;\n        }\n\n        return this.getRange().isMultiLine();\n    };\n\n    this.getCursor = function() {\n        return this.selectionLead.getPosition();\n    };\n\n    this.setSelectionAnchor = function(row, column) {\n        this.selectionAnchor.setPosition(row, column);\n\n        if (this.$isEmpty) {\n            this.$isEmpty = false;\n            this._dispatchEvent(\"changeSelection\");\n        }\n    };\n\n    this.getSelectionAnchor = function() {\n        if (this.$isEmpty)\n            return this.getSelectionLead()\n        else\n            return this.selectionAnchor.getPosition();\n    };\n\n    this.getSelectionLead = function() {\n        return this.selectionLead.getPosition();\n    };\n\n    this.shiftSelection = function(columns) {\n        if (this.$isEmpty) {\n            this.moveCursorTo(this.selectionLead.row, this.selectionLead.column + columns);\n            return;\n        };\n\n        var anchor = this.getSelectionAnchor();\n        var lead = this.getSelectionLead();\n\n        var isBackwards = this.isBackwards();\n\n        if (!isBackwards || anchor.column !== 0)\n            this.setSelectionAnchor(anchor.row, anchor.column + columns);\n\n        if (isBackwards || lead.column !== 0) {\n            this.$moveSelection(function() {\n                this.moveCursorTo(lead.row, lead.column + columns);\n            });\n        }\n    };\n\n    this.isBackwards = function() {\n        var anchor = this.selectionAnchor;\n        var lead = this.selectionLead;\n        return (anchor.row > lead.row || (anchor.row == lead.row && anchor.column > lead.column));\n    };\n\n    this.getRange = function() {\n        var anchor = this.selectionAnchor;\n        var lead = this.selectionLead;\n\n        if (this.isEmpty())\n            return Range.fromPoints(lead, lead);\n\n        if (this.isBackwards()) {\n            return Range.fromPoints(lead, anchor);\n        }\n        else {\n            return Range.fromPoints(anchor, lead);\n        }\n    };\n\n    this.clearSelection = function() {\n        if (!this.$isEmpty) {\n            this.$isEmpty = true;\n            this._dispatchEvent(\"changeSelection\");\n        }\n    };\n\n    this.selectAll = function() {\n        var lastRow = this.doc.getLength() - 1;\n        this.setSelectionAnchor(lastRow, this.doc.getLine(lastRow).length);\n        this.moveCursorTo(0, 0);\n    };\n\n    this.setSelectionRange = function(range, reverse) {\n        if (reverse) {\n            this.setSelectionAnchor(range.end.row, range.end.column);\n            this.selectTo(range.start.row, range.start.column);\n        } else {\n            this.setSelectionAnchor(range.start.row, range.start.column);\n            this.selectTo(range.end.row, range.end.column);\n        }\n        this.$updateDesiredColumn();\n    };\n\n    this.$updateDesiredColumn = function() {\n        var cursor = this.getCursor();\n        this.$desiredColumn = this.session.documentToScreenColumn(cursor.row, cursor.column);\n    };\n\n    this.$moveSelection = function(mover) {\n        var lead = this.selectionLead;\n        if (this.$isEmpty)\n            this.setSelectionAnchor(lead.row, lead.column);\n\n        mover.call(this);\n    };\n\n    this.selectTo = function(row, column) {\n        this.$moveSelection(function() {\n            this.moveCursorTo(row, column);\n        });\n    };\n\n    this.selectToPosition = function(pos) {\n        this.$moveSelection(function() {\n            this.moveCursorToPosition(pos);\n        });\n    };\n\n    this.selectUp = function() {\n        this.$moveSelection(this.moveCursorUp);\n    };\n\n    this.selectDown = function() {\n        this.$moveSelection(this.moveCursorDown);\n    };\n\n    this.selectRight = function() {\n        this.$moveSelection(this.moveCursorRight);\n    };\n\n    this.selectLeft = function() {\n        this.$moveSelection(this.moveCursorLeft);\n    };\n\n    this.selectLineStart = function() {\n        this.$moveSelection(this.moveCursorLineStart);\n    };\n\n    this.selectLineEnd = function() {\n        this.$moveSelection(this.moveCursorLineEnd);\n    };\n\n    this.selectFileEnd = function() {\n        this.$moveSelection(this.moveCursorFileEnd);\n    };\n\n    this.selectFileStart = function() {\n        this.$moveSelection(this.moveCursorFileStart);\n    };\n\n    this.selectWordRight = function() {\n        this.$moveSelection(this.moveCursorWordRight);\n    };\n\n    this.selectWordLeft = function() {\n        this.$moveSelection(this.moveCursorWordLeft);\n    };\n\n    this.selectWord = function() {\n        var cursor = this.getCursor();\n        var range  = this.session.getWordRange(cursor.row, cursor.column);\n        this.setSelectionRange(range);\n    };\n\n    this.selectLine = function() {\n        this.setSelectionAnchor(this.selectionLead.row, 0);\n        this.$moveSelection(function() {\n            this.moveCursorTo(this.selectionLead.row + 1, 0);\n        });\n    };\n\n    this.moveCursorUp = function() {\n        this.moveCursorBy(-1, 0);\n    };\n\n    this.moveCursorDown = function() {\n        this.moveCursorBy(1, 0);\n    };\n\n    this.moveCursorLeft = function() {\n        var cursor = this.selectionLead.getPosition();\n        if (cursor.column == 0) {\n            // cursor is a line (start\n            if (cursor.row > 0) {\n                this.moveCursorTo(cursor.row - 1, this.doc.getLine(cursor.row - 1).length);\n            }\n        }\n        else {\n            var tabSize = this.session.getTabSize();\n            if (this.session.isTabStop(cursor) && this.doc.getLine(cursor.row).slice(cursor.column-tabSize, cursor.column).split(\" \").length-1 == tabSize)\n                this.moveCursorBy(0, -tabSize);\n            else\n                this.moveCursorBy(0, -1);\n        }\n    };\n\n    this.moveCursorRight = function() {\n        if (this.selectionLead.column == this.doc.getLine(this.selectionLead.row).length) {\n            if (this.selectionLead.row < this.doc.getLength() - 1) {\n                this.moveCursorTo(this.selectionLead.row + 1, 0);\n            }\n        }\n        else {\n            var tabSize = this.session.getTabSize();\n            var cursor = this.selectionLead;\n            if (this.session.isTabStop(cursor) && this.doc.getLine(cursor.row).slice(cursor.column, cursor.column+tabSize).split(\" \").length-1 == tabSize)\n                this.moveCursorBy(0, tabSize);\n            else\n                this.moveCursorBy(0, 1);\n        }\n    };\n\n    this.moveCursorLineStart = function() {\n        var row = this.selectionLead.row;\n        var column = this.selectionLead.column;\n        var screenRow = this.session.documentToScreenRow(row, column);\n        var firstRowColumn = this.session.getScreenFirstRowColumn(screenRow);\n        var beforeCursor = this.doc.getLine(row).slice(firstRowColumn, column);\n        var leadingSpace = beforeCursor.match(/^\\s*/);\n        if (leadingSpace[0].length == 0) {\n            var lastRowColumn = this.session.getDocumentLastRowColumn(row, column);\n            leadingSpace = this.doc.getLine(row).\n                                substring(firstRowColumn, lastRowColumn).\n                                match(/^\\s*/);\n            this.moveCursorTo(row, firstRowColumn + leadingSpace[0].length);\n        } else if (leadingSpace[0].length >= column) {\n            this.moveCursorTo(row, firstRowColumn);\n        } else {\n            this.moveCursorTo(row, firstRowColumn + leadingSpace[0].length);\n        }\n    };\n\n    this.moveCursorLineEnd = function() {\n        var lead = this.selectionLead;\n        this.moveCursorTo(lead.row, this.session.getDocumentLastRowColumn(lead.row, lead.column));\n    };\n\n    this.moveCursorFileEnd = function() {\n        var row = this.doc.getLength() - 1;\n        var column = this.doc.getLine(row).length;\n        this.moveCursorTo(row, column);\n    };\n\n    this.moveCursorFileStart = function() {\n        this.moveCursorTo(0, 0);\n    };\n\n    this.moveCursorWordRight = function() {\n        var row = this.selectionLead.row;\n        var column = this.selectionLead.column;\n        var line = this.doc.getLine(row);\n        var rightOfCursor = line.substring(column);\n\n        var match;\n        this.session.nonTokenRe.lastIndex = 0;\n        this.session.tokenRe.lastIndex = 0;\n\n        if (column == line.length) {\n            this.moveCursorRight();\n            return;\n        }\n        else if (match = this.session.nonTokenRe.exec(rightOfCursor)) {\n            column += this.session.nonTokenRe.lastIndex;\n            this.session.nonTokenRe.lastIndex = 0;\n        }\n        else if (match = this.session.tokenRe.exec(rightOfCursor)) {\n            column += this.session.tokenRe.lastIndex;\n            this.session.tokenRe.lastIndex = 0;\n        }\n\n        this.moveCursorTo(row, column);\n    };\n\n    this.moveCursorWordLeft = function() {\n        var row = this.selectionLead.row;\n        var column = this.selectionLead.column;\n        var line = this.doc.getLine(row);\n        var leftOfCursor = lang.stringReverse(line.substring(0, column));\n\n        var match;\n        this.session.nonTokenRe.lastIndex = 0;\n        this.session.tokenRe.lastIndex = 0;\n\n        if (column == 0) {\n            this.moveCursorLeft();\n            return;\n        }\n        else if (match = this.session.nonTokenRe.exec(leftOfCursor)) {\n            column -= this.session.nonTokenRe.lastIndex;\n            this.session.nonTokenRe.lastIndex = 0;\n        }\n        else if (match = this.session.tokenRe.exec(leftOfCursor)) {\n            column -= this.session.tokenRe.lastIndex;\n            this.session.tokenRe.lastIndex = 0;\n        }\n\n        this.moveCursorTo(row, column);\n    };\n\n    this.moveCursorBy = function(rows, chars) {\n        if (this.session.getUseWrapMode()) {\n            var screenPos = this.session.documentToScreenPosition(\n                this.selectionLead.row, \n                this.selectionLead.column\n            );\n            var screenCol = (chars == 0 && this.$desiredColumn) || screenPos.column;\n            var docPos = this.session.screenToDocumentPosition(screenPos.row + rows, screenCol);\n            \n            this.moveCursorTo(docPos.row, docPos.column + chars, chars == 0);\n        } else {\n            var docColumn = (chars == 0 && this.$desiredColumn) || this.selectionLead.column;\n            this.moveCursorTo(this.selectionLead.row + rows, docColumn + chars, chars == 0);\n        }\n    };\n\n    this.moveCursorToPosition = function(position) {\n        this.moveCursorTo(position.row, position.column);\n    };\n\n    this.moveCursorTo = function(row, column, preventUpdateDesiredColumn) {\n        this.selectionLead.setPosition(row, column);\n        if (!preventUpdateDesiredColumn)\n            this.$updateDesiredColumn(this.selectionLead.column);\n    };\n\n    this.moveCursorToScreen = function(row, column, preventUpdateDesiredColumn) {\n        if (this.session.getUseWrapMode()) {\n            var pos = this.session.screenToDocumentPosition(row, column);\n            row = pos.row;\n            column = pos.column;\n        }\n        this.moveCursorTo(row, column, preventUpdateDesiredColumn);\n    };\n\n}).call(Selection.prototype);\n\nexports.Selection = Selection;\n});\n/* ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Ajax.org Code Editor (ACE).\n *\n * The Initial Developer of the Original Code is\n * Ajax.org B.V.\n * Portions created by the Initial Developer are Copyright (C) 2010\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *      Fabian Jakobs <fabian AT ajax DOT org>\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('ace/range', function(require, exports, module) {\n\nvar Range = function(startRow, startColumn, endRow, endColumn) {\n    this.start = {\n        row: startRow,\n        column: startColumn\n    };\n\n    this.end = {\n        row: endRow,\n        column: endColumn\n    };\n};\n\n(function() {\n\n    this.toString = function() {\n        return (\"Range: [\" + this.start.row + \"/\" + this.start.column +\n            \"] -> [\" + this.end.row + \"/\" + this.end.column + \"]\");\n    };\n\n    this.contains = function(row, column) {\n        return this.compare(row, column) == 0;\n    };\n\n    this.compare = function(row, column) {\n        if (!this.isMultiLine()) {\n            if (row === this.start.row) {\n                return column < this.start.column ? -1 : (column > this.end.column ? 1 : 0);\n            };\n        }\n\n        if (row < this.start.row)\n            return -1;\n\n        if (row > this.end.row)\n            return 1;\n\n        if (this.start.row === row)\n            return column >= this.start.column ? 0 : -1;\n\n        if (this.end.row === row)\n            return column <= this.end.column ? 0 : 1;\n\n        return 0;\n    };\n\n    this.clipRows = function(firstRow, lastRow) {\n        if (this.end.row > lastRow) {\n            var end = {\n                row: lastRow+1,\n                column: 0\n            };\n        }\n\n        if (this.start.row > lastRow) {\n            var start = {\n                row: lastRow+1,\n                column: 0\n            };\n        }\n\n        if (this.start.row < firstRow) {\n            var start = {\n                row: firstRow,\n                column: 0\n            };\n        }\n\n        if (this.end.row < firstRow) {\n            var end = {\n                row: firstRow,\n                column: 0\n            };\n        }\n        return Range.fromPoints(start || this.start, end || this.end);\n    };\n\n    this.extend = function(row, column) {\n        var cmp = this.compare(row, column);\n\n        if (cmp == 0)\n            return this;\n        else if (cmp == -1)\n            var start = {row: row, column: column};\n        else\n            var end = {row: row, column: column};\n\n        return Range.fromPoints(start || this.start, end || this.end);\n    };\n\n    this.isEmpty = function() {\n        return (this.start.row == this.end.row && this.start.column == this.end.column);\n    };\n\n    this.isMultiLine = function() {\n        return (this.start.row !== this.end.row);\n    };\n\n    this.clone = function() {\n        return Range.fromPoints(this.start, this.end);\n    };\n\n    this.collapseRows = function() {\n        if (this.end.column == 0)\n            return new Range(this.start.row, 0, Math.max(this.start.row, this.end.row-1), 0)\n        else\n            return new Range(this.start.row, 0, this.end.row, 0)\n    };\n\n    this.toScreenRange = function(session) {\n        var screenPosStart =\n            session.documentToScreenPosition(this.start);\n        var screenPosEnd =\n            session.documentToScreenPosition(this.end);\n        return new Range(\n            screenPosStart.row, screenPosStart.column,\n            screenPosEnd.row, screenPosEnd.column\n        );\n    };\n\n}).call(Range.prototype);\n\n\nRange.fromPoints = function(start, end) {\n    return new Range(start.row, start.column, end.row, end.column);\n};\n\nexports.Range = Range;\n});\n/* vim:ts=4:sts=4:sw=4:\n * ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Ajax.org Code Editor (ACE).\n *\n * The Initial Developer of the Original Code is\n * Ajax.org B.V.\n * Portions created by the Initial Developer are Copyright (C) 2010\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *      Fabian Jakobs <fabian AT ajax DOT org>\n *      Mihai Sucan <mihai DOT sucan AT gmail DOT com>\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('ace/mode/text', function(require, exports, module) {\n\nvar Tokenizer = require(\"ace/tokenizer\").Tokenizer;\nvar TextHighlightRules = require(\"ace/mode/text_highlight_rules\").TextHighlightRules;\n\nvar Mode = function() {\n    this.$tokenizer = new Tokenizer(new TextHighlightRules().getRules());\n};\n\n(function() {\n\n    this.getTokenizer = function() {\n        return this.$tokenizer;\n    };\n\n    this.toggleCommentLines = function(state, doc, startRow, endRow) {\n    };\n\n    this.getNextLineIndent = function(state, line, tab) {\n        return \"\";\n    };\n\n    this.checkOutdent = function(state, line, input) {\n        return false;\n    };\n\n    this.autoOutdent = function(state, doc, row) {\n    };\n\n    this.$getIndent = function(line) {\n        var match = line.match(/^(\\s+)/);\n        if (match) {\n            return match[1];\n        }\n\n        return \"\";\n    };\n    \n    this.createWorker = function(session) {\n        return null;\n    };\n\n    this.highlightSelection = function(editor) {\n        var session = editor.session;\n        if (!session.$selectionOccurrences)\n            session.$selectionOccurrences = [];\n\n        if (session.$selectionOccurrences.length)\n            this.clearSelectionHighlight(editor);\n\n        var selection = editor.getSelectionRange();\n        if (selection.isEmpty() || selection.isMultiLine())\n            return;\n\n        var startOuter = selection.start.column - 1;\n        var endOuter = selection.end.column + 1;\n        var line = session.getLine(selection.start.row);\n        var lineCols = line.length;\n        var needle = line.substring(Math.max(startOuter, 0),\n                                    Math.min(endOuter, lineCols));\n\n        // Make sure the outer characters are not part of the word.\n        if ((startOuter >= 0 && /^[\\w\\d]/.test(needle)) ||\n            (endOuter <= lineCols && /[\\w\\d]$/.test(needle)))\n            return;\n\n        needle = line.substring(selection.start.column, selection.end.column);\n        if (!/^[\\w\\d]+$/.test(needle))\n            return;\n\n        var cursor = editor.getCursorPosition();\n\n        var newOptions = {\n            wrap: true,\n            wholeWord: true,\n            caseSensitive: true,\n            needle: needle\n        };\n\n        var currentOptions = editor.$search.getOptions();\n        editor.$search.set(newOptions);\n\n        var ranges = editor.$search.findAll(session);\n        ranges.forEach(function(range) {\n            if (!range.contains(cursor.row, cursor.column)) {\n                var marker = session.addMarker(range, \"ace_selected_word\");\n                session.$selectionOccurrences.push(marker);\n            }\n        });\n\n        editor.$search.set(currentOptions);\n    };\n\n    this.clearSelectionHighlight = function(editor) {\n        if (!editor.session.$selectionOccurrences)\n            return;\n\n        editor.session.$selectionOccurrences.forEach(function(marker) {\n            editor.session.removeMarker(marker);\n        });\n\n        editor.session.$selectionOccurrences = [];\n    };\n\n}).call(Mode.prototype);\n\nexports.Mode = Mode;\n});\n/* ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Ajax.org Code Editor (ACE).\n *\n * The Initial Developer of the Original Code is\n * Ajax.org B.V.\n * Portions created by the Initial Developer are Copyright (C) 2010\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *      Fabian Jakobs <fabian AT ajax DOT org>\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('ace/tokenizer', function(require, exports, module) {\n\nvar Tokenizer = function(rules) {\n    this.rules = rules;\n\n    this.regExps = {};\n    for ( var key in this.rules) {\n        var rule = this.rules[key];\n        var state = rule;\n        var ruleRegExps = [];\n\n        for ( var i = 0; i < state.length; i++)\n            ruleRegExps.push(state[i].regex);\n\n        this.regExps[key] = new RegExp(\"(?:(\" + ruleRegExps.join(\")|(\") + \")|(.))\", \"g\");\n        \n    }\n};\n\n(function() {\n\n    this.getLineTokens = function(line, startState) {\n        var currentState = startState;\n        var state = this.rules[currentState];\n        var re = this.regExps[currentState];\n        re.lastIndex = 0;\n\n        var match, tokens = [];\n\n        var lastIndex = 0;\n\n        var token = {\n            type: null,\n            value: \"\"\n        };\n\n        while (match = re.exec(line)) {\n            var type = \"text\";\n            var value = match[0];\n\n            for ( var i = 0; i < state.length; i++) {\n                if (match[i + 1]) {\n                    var rule = state[i];\n                    \n                    if (typeof rule.token == \"function\")\n                        type = rule.token(match[0]);\n                    else\n                        type = rule.token;\n\n                    if (rule.next && rule.next !== currentState) {\n                        currentState = rule.next;\n                        state = this.rules[currentState];\n                        lastIndex = re.lastIndex;\n\n                        re = this.regExps[currentState];\n                        re.lastIndex = lastIndex;\n                    }\n                    break;\n                }\n            };\n            \n                  \n            if (token.type !== type) {\n                if (token.type)\n                    tokens.push(token);\n                    \n                token = {\n                    type: type,\n                    value: value\n                };\n            } else {\n                token.value += value;\n            }\n            \n            if (lastIndex == line.length)\n                break;\n            \n            lastIndex = re.lastIndex;\n        };\n\n        if (token.type)\n            tokens.push(token);\n\n        return {\n            tokens : tokens,\n            state : currentState\n        };\n    };\n\n}).call(Tokenizer.prototype);\n\nexports.Tokenizer = Tokenizer;\n});\n/* ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Ajax.org Code Editor (ACE).\n *\n * The Initial Developer of the Original Code is\n * Ajax.org B.V.\n * Portions created by the Initial Developer are Copyright (C) 2010\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *      Fabian Jakobs <fabian AT ajax DOT org>\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('ace/mode/text_highlight_rules', function(require, exports, module) {\n\nvar TextHighlightRules = function() {\n\n    // regexp must not have capturing parentheses\n    // regexps are ordered -> the first match is used\n\n    this.$rules = {\n        \"start\" : [ {\n            token : \"empty_line\",\n            regex : '^$'\n        }, {\n            token : \"text\",\n            regex : \".+\"\n        } ]\n    };\n};\n\n(function() {\n\n    this.addRules = function(rules, prefix) {\n        for (var key in rules) {\n            var state = rules[key];\n            for (var i=0; i<state.length; i++) {\n                var rule = state[i];\n                if (rule.next) {\n                    rule.next = prefix + rule.next;\n                } else {\n                    rule.next = prefix + key;\n                }\n            }\n            this.$rules[prefix + key] = state;\n        }\n    };\n\n    this.getRules = function() {\n        return this.$rules;\n    };\n\n}).call(TextHighlightRules.prototype);\n\nexports.TextHighlightRules = TextHighlightRules;\n});\n/* ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Ajax.org Code Editor (ACE).\n *\n * The Initial Developer of the Original Code is\n * Ajax.org B.V.\n * Portions created by the Initial Developer are Copyright (C) 2010\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *      Fabian Jakobs <fabian AT ajax DOT org>\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('ace/document', function(require, exports, module) {\n\nvar oop = require(\"pilot/oop\");\nvar EventEmitter = require(\"pilot/event_emitter\").EventEmitter;\nvar Range = require(\"ace/range\").Range;\nvar Anchor = require(\"ace/anchor\").Anchor;\n\nvar Document = function(text) {\n    this.$lines = [];\n\n    if (Array.isArray(text)) {\n        this.insertLines(0, text);\n    }\n    // There has to be one line at least in the document. If you pass an empty\n    // string to the insert function, nothing will happen. Workaround.\n    else if (text.length == 0) {\n        this.$lines = [\"\"];\n    } else {\n        this.insert({row: 0, column:0}, text);\n    }\n};\n\n(function() {\n\n    oop.implement(this, EventEmitter);\n\n    this.setValue = function(text) {\n        var len = this.getLength();\n        this.remove(new Range(0, 0, len, this.getLine(len-1).length));\n        this.insert({row: 0, column:0}, text);\n    };\n  \t\n    this.getValue = function() {\n        return this.getAllLines().join(this.getNewLineCharacter());\n    };\n    \n    this.createAnchor = function(row, column) {\n        return new Anchor(this, row, column);\n    };\n\n    // check for IE split bug\n    if (\"aaa\".split(/a/).length == 0)\n        this.$split = function(text) {\n            return text.replace(/\\r\\n|\\r/g, \"\\n\").split(\"\\n\");\n        }\n    else\n        this.$split = function(text) {\n            return text.split(/\\r\\n|\\r|\\n/);\n        };\n\n\n    this.$detectNewLine = function(text) {\n        var match = text.match(/^.*?(\\r?\\n)/m);\n        if (match) {\n            this.$autoNewLine = match[1];\n        } else {\n            this.$autoNewLine = \"\\n\";\n        }\n    };\n\n    this.getNewLineCharacter = function() {\n      switch (this.$newLineMode) {\n          case \"windows\":\n              return \"\\r\\n\";\n\n          case \"unix\":\n              return \"\\n\";\n\n          case \"auto\":\n              return this.$autoNewLine;\n      }\n    },\n\n    this.$autoNewLine = \"\\n\";\n    this.$newLineMode = \"auto\";\n    this.setNewLineMode = function(newLineMode) {\n        if (this.$newLineMode === newLineMode) return;\n\n        this.$newLineMode = newLineMode;\n    };\n\n    this.getNewLineMode = function() {\n        return this.$newLineMode;\n    };\n\n    this.isNewLine = function(text) {\n        return (text == \"\\r\\n\" || text == \"\\r\" || text == \"\\n\");\n    };\n\n    /**\n     * Get a verbatim copy of the given line as it is in the document\n     */\n    this.getLine = function(row) {\n        return this.getLines(row, row + 1)[0] || \"\";\n    };\n\n    this.getLines = function(firstRow, lastRow) {\n        return this.$lines.slice(firstRow, lastRow + 1);\n    };\n\n    /**\n     * Returns all lines in the document as string array. Warning: The caller\n     * should not modify this array!\n     */\n    this.getAllLines = function() {\n        return this.getLines(0, this.getLength());\n    };\n\n    this.getLength = function() {\n        return this.$lines.length;\n    };\n\n    this.getTextRange = function(range) {\n        if (range.start.row == range.end.row) {\n            return this.$lines[range.start.row].substring(range.start.column,\n                                                         range.end.column);\n        }\n        else {\n            var lines = [];\n            lines.push(this.$lines[range.start.row].substring(range.start.column));\n            lines.push.apply(lines, this.getLines(range.start.row+1, range.end.row-1));\n            lines.push(this.$lines[range.end.row].substring(0, range.end.column));\n            return lines.join(this.getNewLineCharacter());\n        }\n    };\n\n    this.$clipPosition = function(position) {\n        var length = this.getLength();\n        if (position.row >= length) {\n            position.row = Math.max(0, length - 1);\n            position.column = this.getLine(length-1).length;\n        }\n        return position;\n    }\n\n    this.insert = function(position, text) {\n        if (text.length == 0)\n            return position;\n\n        position = this.$clipPosition(position);\n\n        if (this.getLength() <= 1)\n            this.$detectNewLine(text);\n\n        var lines = this.$split(text);\n        var firstLine = lines.splice(0, 1)[0];\n        var lastLine = lines.length == 0 ? null : lines.splice(lines.length - 1, 1)[0];\n\n        position = this.insertInLine(position, firstLine);\n        if (lastLine !== null) {\n            position = this.insertNewLine(position); // terminate first line\n            position = this.insertLines(position.row, lines);\n            position = this.insertInLine(position, lastLine || \"\");\n        }\n        return position;\n    };\n\n    this.insertLines = function(row, lines) {\n        if (lines.length == 0)\n            return {row: row, column: 0};\n\n        var args = [row, 0];\n        args.push.apply(args, lines);\n        this.$lines.splice.apply(this.$lines, args);\n\n        var range = new Range(row, 0, row + lines.length, 0);\n        var delta = {\n            action: \"insertLines\",\n            range: range,\n            lines: lines\n        };\n        this._dispatchEvent(\"change\", { data: delta });\n        return range.end;\n    },\n\n    this.insertNewLine = function(position) {\n        position = this.$clipPosition(position);\n        var line = this.$lines[position.row] || \"\";\n        this.$lines[position.row] = line.substring(0, position.column);\n        this.$lines.splice(position.row + 1, 0, line.substring(position.column, line.length));\n\n        var end = {\n            row : position.row + 1,\n            column : 0\n        };\n\n        var delta = {\n            action: \"insertText\",\n            range: Range.fromPoints(position, end),\n            text: this.getNewLineCharacter()\n        };\n        this._dispatchEvent(\"change\", { data: delta });\n\n        return end;\n    };\n\n    this.insertInLine = function(position, text) {\n        if (text.length == 0)\n            return position;\n\n        var line = this.$lines[position.row] || \"\";\n        this.$lines[position.row] = line.substring(0, position.column) + text\n                + line.substring(position.column);\n\n        var end = {\n            row : position.row,\n            column : position.column + text.length\n        };\n\n        var delta = {\n            action: \"insertText\",\n            range: Range.fromPoints(position, end),\n            text: text\n        };\n        this._dispatchEvent(\"change\", { data: delta });\n\n        return end;\n    };\n\n    this.remove = function(range) {\n        // clip to document\n        range.start = this.$clipPosition(range.start);\n        range.end = this.$clipPosition(range.end);\n\n        if (range.isEmpty())\n            return range.start;\n\n        var firstRow = range.start.row;\n        var lastRow = range.end.row;\n\n        if (range.isMultiLine()) {\n            var firstFullRow = range.start.column == 0 ? firstRow : firstRow + 1;\n            var lastFullRow = lastRow - 1;\n\n            if (range.end.column > 0)\n                this.removeInLine(lastRow, 0, range.end.column);\n\n            if (lastFullRow >= firstFullRow)\n                this.removeLines(firstFullRow, lastFullRow);\n\n            if (firstFullRow != firstRow) {\n                this.removeInLine(firstRow, range.start.column, this.getLine(firstRow).length);\n                this.removeNewLine(range.start.row);\n            }\n        }\n        else {\n            this.removeInLine(firstRow, range.start.column, range.end.column);\n        }\n        return range.start;\n    };\n\n    this.removeInLine = function(row, startColumn, endColumn) {\n        if (startColumn == endColumn)\n            return;\n\n        var range = new Range(row, startColumn, row, endColumn);\n        var line = this.getLine(row);\n        var removed = line.substring(startColumn, endColumn);\n        var newLine = line.substring(0, startColumn) + line.substring(endColumn, line.length);\n        this.$lines.splice(row, 1, newLine);\n\n        var delta = {\n            action: \"removeText\",\n            range: range,\n            text: removed\n        };\n        this._dispatchEvent(\"change\", { data: delta });\n        return range.start;\n    };\n\n    /**\n     * Removes a range of full lines\n     *\n     * @param firstRow {Integer} The first row to be removed\n     * @param lastRow {Integer} The last row to be removed\n     * @return {String[]} The removed lines\n     */\n    this.removeLines = function(firstRow, lastRow) {\n        var range = new Range(firstRow, 0, lastRow + 1, 0);\n        var removed = this.$lines.splice(firstRow, lastRow - firstRow + 1);\n\n        var delta = {\n            action: \"removeLines\",\n            range: range,\n            nl: this.getNewLineCharacter(),\n            lines: removed\n        };\n        this._dispatchEvent(\"change\", { data: delta });\n        return removed;\n    };\n\n    this.removeNewLine = function(row) {\n        var firstLine = this.getLine(row);\n        var secondLine = this.getLine(row+1);\n\n        var range = new Range(row, firstLine.length, row+1, 0);\n        var line = firstLine + secondLine;\n\n        this.$lines.splice(row, 2, line);\n\n        var delta = {\n            action: \"removeText\",\n            range: range,\n            text: this.getNewLineCharacter()\n        };\n        this._dispatchEvent(\"change\", { data: delta });\n    };\n\n    this.replace = function(range, text) {\n        if (text.length == 0 && range.isEmpty())\n            return range.start;\n\n        // Shortcut: If the text we want to insert is the same as it is already\n        // in the document, we don't have to replace anything.\n        if (text == this.getTextRange(range))\n            return range.end;\n\n        this.remove(range);\n        if (text) {\n            var end = this.insert(range.start, text);\n        }\n        else {\n            end = range.start;\n        }\n\n        return end;\n    };\n\n    this.applyDeltas = function(deltas) {\n        for (var i=0; i<deltas.length; i++) {\n            var delta = deltas[i];\n            var range = Range.fromPoints(delta.range.start, delta.range.end);\n\n            if (delta.action == \"insertLines\")\n                this.insertLines(range.start.row, delta.lines)\n            else if (delta.action == \"insertText\")\n                this.insert(range.start, delta.text)\n            else if (delta.action == \"removeLines\")\n                this.removeLines(range.start.row, range.end.row - 1)\n            else if (delta.action == \"removeText\")\n                this.remove(range)\n        }\n    };\n\n    this.revertDeltas = function(deltas) {\n        for (var i=deltas.length-1; i>=0; i--) {\n            var delta = deltas[i];\n            var range = Range.fromPoints(delta.range.start, delta.range.end);\n\n            if (delta.action == \"insertLines\")\n                this.removeLines(range.start.row, range.end.row - 1)\n            else if (delta.action == \"insertText\")\n                this.remove(range)\n            else if (delta.action == \"removeLines\")\n                this.insertLines(range.start.row, delta.lines)\n            else if (delta.action == \"removeText\")\n                this.insert(range.start, delta.text)\n        }\n    };\n\n}).call(Document.prototype);\n\nexports.Document = Document;\n});\n/* ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Ajax.org Code Editor (ACE).\n *\n * The Initial Developer of the Original Code is\n * Ajax.org B.V.\n * Portions created by the Initial Developer are Copyright (C) 2010\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *      Fabian Jakobs <fabian AT ajax DOT org>\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('ace/anchor', function(require, exports, module) {\n\nvar oop = require(\"pilot/oop\");\nvar EventEmitter = require(\"pilot/event_emitter\").EventEmitter;\n\n/**\n * An Anchor is a floating pointer in the document. Whenever text is inserted or\n * deleted before the cursor, the position of the cursor is updated\n */\nvar Anchor = exports.Anchor = function(doc, row, column) {\n    this.document = doc;\n    \n    if (typeof column == \"undefined\")\n        this.setPosition(row.row, row.column);\n    else\n        this.setPosition(row, column);\n\n    this.$onChange = this.onChange.bind(this);\n    doc.on(\"change\", this.$onChange);\n};\n\n(function() {\n\n    oop.implement(this, EventEmitter);\n    \n    this.getPosition = function() {\n        return this.$clipPositionToDocument(this.row, this.column);\n    };\n    \n    this.getDocument = function() {\n        return this.document;\n    };\n    \n    this.onChange = function(e) {\n        var delta = e.data;\n        var range = delta.range;\n            \n        if (range.start.row == range.end.row && range.start.row != this.row)\n            return;\n            \n        if (range.start.row > this.row)\n            return;\n            \n        if (range.start.row == this.row && range.start.column > this.column)\n            return;\n    \n        var row = this.row;\n        var column = this.column;\n        \n        if (delta.action === \"insertText\") {\n            if (range.start.row === row && range.start.column <= column) {\n                if (range.start.row === range.end.row) {\n                    column += range.end.column - range.start.column;\n                }\n                else {\n                    column -= range.start.column;\n                    row += range.end.row - range.start.row;\n                }\n            }\n            else if (range.start.row !== range.end.row && range.start.row < row) {\n                row += range.end.row - range.start.row;\n            }\n        } else if (delta.action === \"insertLines\") {\n            if (range.start.row <= row) {\n                row += range.end.row - range.start.row;\n            }\n        }\n        else if (delta.action == \"removeText\") {\n            if (range.start.row == row && range.start.column < column) {\n                if (range.end.column >= column)\n                    column = range.start.column;\n                else\n                    column = Math.max(0, column - (range.end.column - range.start.column));\n                \n            } else if (range.start.row !== range.end.row && range.start.row < row) {\n                if (range.end.row == row) {\n                    column = Math.max(0, column - range.end.column) + range.start.column;\n                }\n                row -= (range.end.row - range.start.row);\n            }\n            else if (range.end.row == row) {\n                row -= range.end.row - range.start.row;\n                column = Math.max(0, column - range.end.column) + range.start.column;\n            }\n        } else if (delta.action == \"removeLines\") {\n            if (range.start.row <= row) {\n                if (range.end.row <= row)\n                    row -= range.end.row - range.start.row;\n                else {\n                    row = range.start.row;\n                    column = 0;\n                }\n            }\n        }\n\n        this.setPosition(row, column, true);\n    };\n\n    this.setPosition = function(row, column, noClip) {\n        if (noClip) {\n            pos = {\n                row: row,\n                column: column\n            };\n        }\n        else {\n            pos = this.$clipPositionToDocument(row, column);\n        }\n        \n        if (this.row == pos.row && this.column == pos.column)\n            return;\n            \n        var old = {\n            row: this.row,\n            column: this.column\n        };\n        \n        this.row = pos.row;\n        this.column = pos.column;\n        this._dispatchEvent(\"change\", {\n            old: old,\n            value: pos\n        });\n    };\n    \n    this.detach = function() {\n        this.document.removeEventListener(\"change\", this.$onChange);\n    };\n    \n    this.$clipPositionToDocument = function(row, column) {\n        var pos = {};\n    \n        if (row >= this.document.getLength()) {\n            pos.row = Math.max(0, this.document.getLength() - 1);\n            pos.column = this.document.getLine(pos.row).length;\n        }\n        else if (row < 0) {\n            pos.row = 0;\n            pos.column = 0;\n        }\n        else {\n            pos.row = row;\n            pos.column = Math.min(this.document.getLine(pos.row).length, Math.max(0, column));\n        }\n        \n        if (column < 0)\n            pos.column = 0;\n            \n        return pos;\n    };\n    \n}).call(Anchor.prototype);\n\n});\n/* vim:ts=4:sts=4:sw=4:\n * ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Ajax.org Code Editor (ACE).\n *\n * The Initial Developer of the Original Code is\n * Ajax.org B.V.\n * Portions created by the Initial Developer are Copyright (C) 2010\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *      Fabian Jakobs <fabian AT ajax DOT org>\n *      Mihai Sucan <mihai DOT sucan AT gmail DOT com>\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('ace/search', function(require, exports, module) {\n\nvar lang = require(\"pilot/lang\");\nvar oop = require(\"pilot/oop\");\nvar Range = require(\"ace/range\").Range;\n\nvar Search = function() {\n    this.$options = {\n        needle: \"\",\n        backwards: false,\n        wrap: false,\n        caseSensitive: false,\n        wholeWord: false,\n        scope: Search.ALL,\n        regExp: false\n    };\n};\n\nSearch.ALL = 1;\nSearch.SELECTION = 2;\n\n(function() {\n\n    this.set = function(options) {\n        oop.mixin(this.$options, options);\n        return this;\n    };\n    \n    this.getOptions = function() {\n        return lang.copyObject(this.$options);\n    };\n\n    this.find = function(session) {\n        if (!this.$options.needle)\n            return null;\n\n        if (this.$options.backwards) {\n            var iterator = this.$backwardMatchIterator(session);\n        } else {\n            iterator = this.$forwardMatchIterator(session);\n        }\n\n        var firstRange = null;\n        iterator.forEach(function(range) {\n            firstRange = range;\n            return true;\n        });\n\n        return firstRange;\n    };\n\n    this.findAll = function(session) {\n        if (!this.$options.needle)\n            return [];\n\n        if (this.$options.backwards) {\n            var iterator = this.$backwardMatchIterator(session);\n        } else {\n            iterator = this.$forwardMatchIterator(session);\n        }\n\n        var ranges = [];\n        iterator.forEach(function(range) {\n            ranges.push(range);\n        });\n\n        return ranges;\n    };\n\n    this.replace = function(input, replacement) {\n        var re = this.$assembleRegExp();\n        var match = re.exec(input);\n        if (match && match[0].length == input.length) {\n            if (this.$options.regExp) {\n                return input.replace(re, replacement);\n            } else {\n                return replacement;\n            }\n        } else {\n            return null;\n        }\n    };\n\n    this.$forwardMatchIterator = function(session) {\n        var re = this.$assembleRegExp();\n        var self = this;\n\n        return {\n            forEach: function(callback) {\n                self.$forwardLineIterator(session).forEach(function(line, startIndex, row) {\n                    if (startIndex) {\n                        line = line.substring(startIndex);\n                    }\n\n                    var matches = [];\n\n                    line.replace(re, function(str) {\n                        var offset = arguments[arguments.length-2];\n                        matches.push({\n                            str: str,\n                            offset: startIndex + offset\n                        });\n                        return str;\n                    });\n\n                    for (var i=0; i<matches.length; i++) {\n                        var match = matches[i];\n                        var range = self.$rangeFromMatch(row, match.offset, match.str.length);\n                        if (callback(range))\n                            return true;\n                    }\n\n                });\n            }\n        };\n    };\n\n    this.$backwardMatchIterator = function(session) {\n        var re = this.$assembleRegExp();\n        var self = this;\n\n        return {\n            forEach: function(callback) {\n                self.$backwardLineIterator(session).forEach(function(line, startIndex, row) {\n                    if (startIndex) {\n                        line = line.substring(startIndex);\n                    }\n\n                    var matches = [];\n\n                    line.replace(re, function(str, offset) {\n                        matches.push({\n                            str: str,\n                            offset: startIndex + offset\n                        });\n                        return str;\n                    });\n\n                    for (var i=matches.length-1; i>= 0; i--) {\n                        var match = matches[i];\n                        var range = self.$rangeFromMatch(row, match.offset, match.str.length);\n                        if (callback(range))\n                            return true;\n                    }\n                });\n            }\n        };\n    };\n\n    this.$rangeFromMatch = function(row, column, length) {\n        return new Range(row, column, row, column+length);\n    };\n\n    this.$assembleRegExp = function() {\n        if (this.$options.regExp) {\n            var needle = this.$options.needle;\n        } else {\n            needle = lang.escapeRegExp(this.$options.needle);\n        }\n\n        if (this.$options.wholeWord) {\n            needle = \"\\\\b\" + needle + \"\\\\b\";\n        }\n\n        var modifier = \"g\";\n        if (!this.$options.caseSensitive) {\n            modifier += \"i\";\n        }\n\n        var re = new RegExp(needle, modifier);\n        return re;\n    };\n\n    this.$forwardLineIterator = function(session) {\n        var searchSelection = this.$options.scope == Search.SELECTION;\n\n        var range = session.getSelection().getRange();\n        var start = session.getSelection().getCursor();\n\n        var firstRow = searchSelection ? range.start.row : 0;\n        var firstColumn = searchSelection ? range.start.column : 0;\n        var lastRow = searchSelection ? range.end.row : session.getLength() - 1;\n\n        var wrap = this.$options.wrap;\n        var inWrap = false;\n\n        function getLine(row) {\n            var line = session.getLine(row);\n            if (searchSelection && row == range.end.row) {\n                line = line.substring(0, range.end.column);\n            }\n            if (inWrap && row == start.row) {\n                line = line.substring(0, start.column);\n            }\n            return line;\n        }\n\n        return {\n            forEach: function(callback) {\n                var row = start.row;\n\n                var line = getLine(row);\n                var startIndex = start.column;\n\n                var stop = false;\n                inWrap = false;\n\n                while (!callback(line, startIndex, row)) {\n\n                    if (stop) {\n                        return;\n                    }\n\n                    row++;\n                    startIndex = 0;\n\n                    if (row > lastRow) {\n                        if (wrap) {\n                            row = firstRow;\n                            startIndex = firstColumn;\n                            inWrap = true;\n                        } else {\n                            return;\n                        }\n                    }\n\n                    if (row == start.row)\n                        stop = true;\n\n                    line = getLine(row);\n                }\n            }\n        };\n    };\n\n    this.$backwardLineIterator = function(session) {\n        var searchSelection = this.$options.scope == Search.SELECTION;\n\n        var range = session.getSelection().getRange();\n        var start = searchSelection ? range.end : range.start;\n\n        var firstRow = searchSelection ? range.start.row : 0;\n        var firstColumn = searchSelection ? range.start.column : 0;\n        var lastRow = searchSelection ? range.end.row : session.getLength() - 1;\n\n        var wrap = this.$options.wrap;\n\n        return {\n            forEach : function(callback) {\n                var row = start.row;\n\n                var line = session.getLine(row).substring(0, start.column);\n                var startIndex = 0;\n                var stop = false;\n                var inWrap = false;\n\n                while (!callback(line, startIndex, row)) {\n\n                    if (stop)\n                        return;\n\n                    row--;\n                    startIndex = 0;\n\n                    if (row < firstRow) {\n                        if (wrap) {\n                            row = lastRow;\n                            inWrap = true;\n                        } else {\n                            return;\n                        }\n                    }\n\n                    if (row == start.row)\n                        stop = true;\n\n                    line = session.getLine(row);\n                    if (searchSelection) {\n                        if (row == firstRow)\n                            startIndex = firstColumn;\n                        else if (row == lastRow)\n                            line = line.substring(0, range.end.column);\n                    }\n\n                    if (inWrap && row == start.row)\n                        startIndex = start.column;\n                }\n            }\n        };\n    };\n\n}).call(Search.prototype);\n\nexports.Search = Search;\n});\n/* ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Ajax.org Code Editor (ACE).\n *\n * The Initial Developer of the Original Code is\n * Ajax.org B.V.\n * Portions created by the Initial Developer are Copyright (C) 2010\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *      Fabian Jakobs <fabian AT ajax DOT org>\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('ace/background_tokenizer', function(require, exports, module) {\n\nvar oop = require(\"pilot/oop\");\nvar EventEmitter = require(\"pilot/event_emitter\").EventEmitter;\n\nvar BackgroundTokenizer = function(tokenizer, editor) {\n    this.running = false;    \n    this.lines = [];\n    this.currentLine = 0;\n    this.tokenizer = tokenizer;\n\n    var self = this;\n\n    this.$worker = function() {\n        if (!self.running) { return; }\n\n        var workerStart = new Date();\n        var startLine = self.currentLine;\n        var doc = self.doc;\n\n        var processedLines = 0;\n        var lastVisibleRow = editor.getLastVisibleRow();\n\n        var len = doc.getLength();\n        while (self.currentLine < len) {\n            self.lines[self.currentLine] = self.$tokenizeRows(self.currentLine, self.currentLine)[0];\n            self.currentLine++;\n\n            // only check every 5 lines\n            processedLines += 1;\n            if ((processedLines % 5 == 0) && (new Date() - workerStart) > 20) {\n                self.fireUpdateEvent(startLine, self.currentLine-1);\n\n                var timeout = self.currentLine < lastVisibleRow ? 20 : 100;\n                self.running = setTimeout(self.$worker, timeout);\n                return;\n            }\n        }\n\n        self.running = false;\n\n        self.fireUpdateEvent(startLine, len - 1);\n    };\n};\n\n(function(){\n\n    oop.implement(this, EventEmitter);\n\n    this.setTokenizer = function(tokenizer) {\n        this.tokenizer = tokenizer;\n        this.lines = [];\n\n        this.start(0);\n    };\n\n    this.setDocument = function(doc) {\n        this.doc = doc;\n        this.lines = [];\n\n        this.stop();\n    };\n\n    this.fireUpdateEvent = function(firstRow, lastRow) {\n        var data = {\n            first: firstRow,\n            last: lastRow\n        };\n        this._dispatchEvent(\"update\", {data: data});\n    };\n\n    this.start = function(startRow) {\n        this.currentLine = Math.min(startRow || 0, this.currentLine,\n                                    this.doc.getLength());\n\n        // remove all cached items below this line\n        this.lines.splice(this.currentLine, this.lines.length);\n\n        this.stop();\n        // pretty long delay to prevent the tokenizer from interfering with the user\n        this.running = setTimeout(this.$worker, 700);\n    };\n\n    this.stop = function() {\n        if (this.running)\n            clearTimeout(this.running);\n        this.running = false;\n    };\n\n    this.getTokens = function(firstRow, lastRow) {\n        return this.$tokenizeRows(firstRow, lastRow);\n    };\n\n    this.getState = function(row) {\n        return this.$tokenizeRows(row, row)[0].state;\n    };\n\n    this.$tokenizeRows = function(firstRow, lastRow) {\n        if (!this.doc)\n            return [];\n            \n        var rows = [];\n\n        // determine start state\n        var state = \"start\";\n        var doCache = false;\n        if (firstRow > 0 && this.lines[firstRow - 1]) {\n            state = this.lines[firstRow - 1].state;\n            doCache = true;\n        }\n\n        var lines = this.doc.getLines(firstRow, lastRow);\n        for (var row=firstRow; row<=lastRow; row++) {\n            if (!this.lines[row]) {\n                var tokens = this.tokenizer.getLineTokens(lines[row-firstRow] || \"\", state);\n                var state = tokens.state;\n                rows.push(tokens);\n\n                if (doCache) {\n                    this.lines[row] = tokens;\n                }\n            }\n            else {\n                var tokens = this.lines[row];\n                state = tokens.state;\n                rows.push(tokens);\n            }\n        }\n        return rows;\n    };\n\n}).call(BackgroundTokenizer.prototype);\n\nexports.BackgroundTokenizer = BackgroundTokenizer;\n});\n/* vim:ts=4:sts=4:sw=4:\n * ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Ajax.org Code Editor (ACE).\n *\n * The Initial Developer of the Original Code is\n * Ajax.org B.V.\n * Portions created by the Initial Developer are Copyright (C) 2010\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *      Fabian Jakobs <fabian AT ajax DOT org>\n *      Mihai Sucan <mihai DOT sucan AT gmail DOT com>\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('ace/undomanager', function(require, exports, module) {\n\nvar UndoManager = function() {\n    this.reset();\n};\n\n(function() {\n\n    this.execute = function(options) {\n        var deltas = options.args[0];\n        this.$doc  = options.args[1];\n        this.$undoStack.push(deltas);\n    };\n\n    this.undo = function() {\n        var deltas = this.$undoStack.pop();\n        if (deltas) {\n            this.$doc.undoChanges(deltas);\n            this.$redoStack.push(deltas);\n        }\n    };\n\n    this.redo = function() {\n        var deltas = this.$redoStack.pop();\n        if (deltas) {\n            this.$doc.redoChanges(deltas);\n            this.$undoStack.push(deltas);\n        }\n    };\n    \n    this.reset = function() {\n        this.$undoStack = [];\n        this.$redoStack = [];\n    };\n\n    this.hasUndo = function() {\n        return this.$undoStack.length > 0;\n    };\n\n    this.hasRedo = function() {\n        return this.$redoStack.length > 0;\n    };\n\n}).call(UndoManager.prototype);\n\nexports.UndoManager = UndoManager;\n});\n/* ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Ajax.org Code Editor (ACE).\n *\n * The Initial Developer of the Original Code is\n * Ajax.org B.V.\n * Portions created by the Initial Developer are Copyright (C) 2010\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *      Fabian Jakobs <fabian AT ajax DOT org>\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('ace/theme/textmate', function(require, exports, module) {\n\n    var dom = require(\"pilot/dom\");\n\n    var cssText = \".ace-tm .ace_editor {\\\n  border: 2px solid rgb(159, 159, 159);\\\n}\\\n\\\n.ace-tm .ace_editor.ace_focus {\\\n  border: 2px solid #327fbd;\\\n}\\\n\\\n.ace-tm .ace_gutter {\\\n  width: 50px;\\\n  background: #e8e8e8;\\\n  color: #333;\\\n  overflow : hidden;\\\n}\\\n\\\n.ace-tm .ace_gutter-layer {\\\n  width: 100%;\\\n  text-align: right;\\\n}\\\n\\\n.ace-tm .ace_gutter-layer .ace_gutter-cell {\\\n  padding-right: 6px;\\\n}\\\n\\\n.ace-tm .ace_print_margin {\\\n  width: 1px;\\\n  background: #e8e8e8;\\\n}\\\n\\\n.ace-tm .ace_text-layer {\\\n  cursor: text;\\\n}\\\n\\\n.ace-tm .ace_cursor {\\\n  border-left: 2px solid black;\\\n}\\\n\\\n.ace-tm .ace_cursor.ace_overwrite {\\\n  border-left: 0px;\\\n  border-bottom: 1px solid black;\\\n}\\\n        \\\n.ace-tm .ace_line .ace_invisible {\\\n  color: rgb(191, 191, 191);\\\n}\\\n\\\n.ace-tm .ace_line .ace_keyword {\\\n  color: blue;\\\n}\\\n\\\n.ace-tm .ace_line .ace_constant.ace_buildin {\\\n  color: rgb(88, 72, 246);\\\n}\\\n\\\n.ace-tm .ace_line .ace_constant.ace_language {\\\n  color: rgb(88, 92, 246);\\\n}\\\n\\\n.ace-tm .ace_line .ace_constant.ace_library {\\\n  color: rgb(6, 150, 14);\\\n}\\\n\\\n.ace-tm .ace_line .ace_invalid {\\\n  background-color: rgb(153, 0, 0);\\\n  color: white;\\\n}\\\n\\\n.ace-tm .ace_line .ace_support.ace_function {\\\n  color: rgb(60, 76, 114);\\\n}\\\n\\\n.ace-tm .ace_line .ace_support.ace_constant {\\\n  color: rgb(6, 150, 14);\\\n}\\\n\\\n.ace-tm .ace_line .ace_support.ace_type,\\\n.ace-tm .ace_line .ace_support.ace_class {\\\n  color: rgb(109, 121, 222);\\\n}\\\n\\\n.ace-tm .ace_line .ace_keyword.ace_operator {\\\n  color: rgb(104, 118, 135);\\\n}\\\n\\\n.ace-tm .ace_line .ace_string {\\\n  color: rgb(3, 106, 7);\\\n}\\\n\\\n.ace-tm .ace_line .ace_comment {\\\n  color: rgb(76, 136, 107);\\\n}\\\n\\\n.ace-tm .ace_line .ace_comment.ace_doc {\\\n  color: rgb(0, 102, 255);\\\n}\\\n\\\n.ace-tm .ace_line .ace_comment.ace_doc.ace_tag {\\\n  color: rgb(128, 159, 191);\\\n}\\\n\\\n.ace-tm .ace_line .ace_constant.ace_numeric {\\\n  color: rgb(0, 0, 205);\\\n}\\\n\\\n.ace-tm .ace_line .ace_variable {\\\n  color: rgb(49, 132, 149);\\\n}\\\n\\\n.ace-tm .ace_line .ace_xml_pe {\\\n  color: rgb(104, 104, 91);\\\n}\\\n\\\n.ace-tm .ace_marker-layer .ace_selection {\\\n  background: rgb(181, 213, 255);\\\n}\\\n\\\n.ace-tm .ace_marker-layer .ace_step {\\\n  background: rgb(252, 255, 0);\\\n}\\\n\\\n.ace-tm .ace_marker-layer .ace_stack {\\\n  background: rgb(164, 229, 101);\\\n}\\\n\\\n.ace-tm .ace_marker-layer .ace_bracket {\\\n  margin: -1px 0 0 -1px;\\\n  border: 1px solid rgb(192, 192, 192);\\\n}\\\n\\\n.ace-tm .ace_marker-layer .ace_active_line {\\\n  background: rgb(232, 242, 254);\\\n}\\\n\\\n.ace-tm .ace_marker-layer .ace_selected_word {\\\n  background: rgb(250, 250, 255);\\\n  border: 1px solid rgb(200, 200, 250);\\\n}\\\n\\\n.ace-tm .ace_string.ace_regex {\\\n  color: rgb(255, 0, 0)\\\n}\";\n\n    // import CSS once\n    dom.importCssString(cssText);\n\n    exports.cssClass = \"ace-tm\";\n});\n/* ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Ajax.org Code Editor (ACE).\n *\n * The Initial Developer of the Original Code is\n * Ajax.org B.V.\n * Portions created by the Initial Developer are Copyright (C) 2010\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *      Fabian Jakobs <fabian AT ajax DOT org>\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('ace/mode/matching_brace_outdent', function(require, exports, module) {\n\nvar Range = require(\"ace/range\").Range;\n\nvar MatchingBraceOutdent = function() {};\n\n(function() {\n\n    this.checkOutdent = function(line, input) {\n        if (! /^\\s+$/.test(line))\n            return false;\n\n        return /^\\s*\\}/.test(input);\n    };\n\n    this.autoOutdent = function(doc, row) {\n        var line = doc.getLine(row);\n        var match = line.match(/^(\\s*\\})/);\n\n        if (!match) return 0;\n\n        var column = match[1].length;\n        var openBracePos = doc.findMatchingBracket({row: row, column: column});\n\n        if (!openBracePos || openBracePos.row == row) return 0;\n\n        var indent = this.$getIndent(doc.getLine(openBracePos.row));\n        doc.replace(new Range(row, 0, row, column-1), indent);\n    };\n\n    this.$getIndent = function(line) {\n        var match = line.match(/^(\\s+)/);\n        if (match) {\n            return match[1];\n        }\n\n        return \"\";\n    };\n\n}).call(MatchingBraceOutdent.prototype);\n\nexports.MatchingBraceOutdent = MatchingBraceOutdent;\n});\n/* vim:ts=4:sts=4:sw=4:\n * ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Ajax.org Code Editor (ACE).\n *\n * The Initial Developer of the Original Code is\n * Ajax.org B.V.\n * Portions created by the Initial Developer are Copyright (C) 2010\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *      Fabian Jakobs <fabian@ajax.org>\n *      Irakli Gozalishvili <rfobic@gmail.com> (http://jeditoolkit.com)\n *      Julian Viereck <julian.viereck@gmail.com>\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('ace/virtual_renderer', function(require, exports, module) {\n\nvar oop = require(\"pilot/oop\");\nvar dom = require(\"pilot/dom\");\nvar event = require(\"pilot/event\");\nvar useragent = require(\"pilot/useragent\");\nvar GutterLayer = require(\"ace/layer/gutter\").Gutter;\nvar MarkerLayer = require(\"ace/layer/marker\").Marker;\nvar TextLayer = require(\"ace/layer/text\").Text;\nvar CursorLayer = require(\"ace/layer/cursor\").Cursor;\nvar ScrollBar = require(\"ace/scrollbar\").ScrollBar;\nvar RenderLoop = require(\"ace/renderloop\").RenderLoop;\nvar EventEmitter = require(\"pilot/event_emitter\").EventEmitter;\nvar editorCss = require(\"text/ace/css/editor.css\");\n\n// import CSS once\ndom.importCssString(editorCss);\n\nvar VirtualRenderer = function(container, theme) {\n    this.container = container;\n    dom.addCssClass(this.container, \"ace_editor\");\n\n    this.setTheme(theme);\n\n    this.$gutter = dom.createElement(\"div\");\n    this.$gutter.className = \"ace_gutter\";\n    this.container.appendChild(this.$gutter);\n\n    this.scroller = dom.createElement(\"div\");\n    this.scroller.className = \"ace_scroller\";\n    this.container.appendChild(this.scroller);\n\n    this.content = dom.createElement(\"div\");\n    this.content.className = \"ace_content\";\n    this.scroller.appendChild(this.content);\n\n    this.$gutterLayer = new GutterLayer(this.$gutter);\n    this.$markerBack = new MarkerLayer(this.content);\n\n    var textLayer = this.$textLayer = new TextLayer(this.content);\n    this.canvas = textLayer.element;\n\n    this.$markerFront = new MarkerLayer(this.content);\n\n    this.characterWidth = textLayer.getCharacterWidth();\n    this.lineHeight = textLayer.getLineHeight();\n\n    this.$cursorLayer = new CursorLayer(this.content);\n    this.$cursorPadding = 8;\n\n    // Indicates whether the horizontal scrollbar is visible\n    this.$horizScroll = true;\n    this.$horizScrollAlwaysVisible = true;\n\n    this.scrollBar = new ScrollBar(container);\n    this.scrollBar.addEventListener(\"scroll\", this.onScroll.bind(this));\n\n    this.scrollTop = 0;\n\n    this.cursorPos = {\n        row : 0,\n        column : 0\n    };\n\n    var _self = this;\n    this.$textLayer.addEventListener(\"changeCharaterSize\", function() {\n        _self.characterWidth = textLayer.getCharacterWidth();\n        _self.lineHeight = textLayer.getLineHeight();\n        _self.$updatePrintMargin();\n        _self.onResize(true);\n\n        _self.$loop.schedule(_self.CHANGE_FULL);\n    });\n    event.addListener(this.$gutter, \"click\", this.$onGutterClick.bind(this));\n    event.addListener(this.$gutter, \"dblclick\", this.$onGutterClick.bind(this));\n\n    this.$size = {\n        width: 0,\n        height: 0,\n        scrollerHeight: 0,\n        scrollerWidth: 0\n    };\n\n    this.$loop = new RenderLoop(this.$renderChanges.bind(this));\n    this.$loop.schedule(this.CHANGE_FULL);\n\n    this.setPadding(4);\n    this.$updatePrintMargin();\n};\n\n(function() {\n    this.showGutter = true;\n\n    this.CHANGE_CURSOR = 1;\n    this.CHANGE_MARKER = 2;\n    this.CHANGE_GUTTER = 4;\n    this.CHANGE_SCROLL = 8;\n    this.CHANGE_LINES = 16;\n    this.CHANGE_TEXT = 32;\n    this.CHANGE_SIZE = 64;\n    this.CHANGE_MARKER_BACK = 128;\n    this.CHANGE_MARKER_FRONT = 256;\n    this.CHANGE_FULL = 512;\n\n    oop.implement(this, EventEmitter);\n\n    this.setSession = function(session) {\n        this.session = session;\n        this.$cursorLayer.setSession(session);\n        this.$markerBack.setSession(session);\n        this.$markerFront.setSession(session);\n        this.$gutterLayer.setSession(session);\n        this.$textLayer.setSession(session);\n        this.$loop.schedule(this.CHANGE_FULL);\n    };\n\n    /**\n     * Triggers partial update of the text layer\n     */\n    this.updateLines = function(firstRow, lastRow) {\n        if (lastRow === undefined)\n            lastRow = Infinity;\n\n        if (!this.$changedLines) {\n            this.$changedLines = {\n                firstRow: firstRow,\n                lastRow: lastRow\n            };\n        }\n        else {\n            if (this.$changedLines.firstRow > firstRow)\n                this.$changedLines.firstRow = firstRow;\n\n            if (this.$changedLines.lastRow < lastRow)\n                this.$changedLines.lastRow = lastRow;\n        }\n\n        this.$loop.schedule(this.CHANGE_LINES);\n    };\n\n    /**\n     * Triggers full update of the text layer\n     */\n    this.updateText = function() {\n        this.$loop.schedule(this.CHANGE_TEXT);\n    };\n\n    /**\n     * Triggers a full update of all layers\n     */\n    this.updateFull = function() {\n        this.$loop.schedule(this.CHANGE_FULL);\n    };\n\n    this.updateFontSize = function() {\n        this.$textLayer.checkForSizeChanges();\n    };\n\n    /**\n     * Triggers resize of the editor\n     */\n    this.onResize = function(force) {\n        var changes = this.CHANGE_SIZE;\n\n        var height = dom.getInnerHeight(this.container);\n        if (force || this.$size.height != height) {\n            this.$size.height = height;\n\n            this.scroller.style.height = height + \"px\";\n            this.scrollBar.setHeight(this.scroller.clientHeight);\n\n            if (this.session) {\n                this.scrollToY(this.getScrollTop());\n                changes = changes | this.CHANGE_FULL;\n            }\n        }\n\n        var width = dom.getInnerWidth(this.container);\n        if (force || this.$size.width != width) {\n            this.$size.width = width;\n\n            var gutterWidth = this.showGutter ? this.$gutter.offsetWidth : 0;\n            this.scroller.style.left = gutterWidth + \"px\";\n            this.scroller.style.width = Math.max(0, width - gutterWidth - this.scrollBar.getWidth()) + \"px\";\n\n            if (this.session.getUseWrapMode()) {\n                var availableWidth = this.scroller.clientWidth - this.$padding * 2;\n                var limit = Math.floor(availableWidth / this.characterWidth) - 1;\n                if (this.session.adjustWrapLimit(limit) || force) {\n                    changes = changes | this.CHANGE_FULL;\n                }\n            }\n        }\n\n        this.$size.scrollerWidth = this.scroller.clientWidth;\n        this.$size.scrollerHeight = this.scroller.clientHeight;\n        this.$loop.schedule(changes);\n    };\n\n    this.setTokenizer = function(tokenizer) {\n        this.$tokenizer = tokenizer;\n        this.$textLayer.setTokenizer(tokenizer);\n        this.$loop.schedule(this.CHANGE_TEXT);\n    };\n\n    this.$onGutterClick = function(e) {\n        var pageX = event.getDocumentX(e);\n        var pageY = event.getDocumentY(e);\n\n        this._dispatchEvent(\"gutter\" + e.type, {\n            row: this.screenToTextCoordinates(pageX, pageY).row,\n            htmlEvent: e\n        });\n    };\n\n    this.setShowInvisibles = function(showInvisibles) {\n        if (this.$textLayer.setShowInvisibles(showInvisibles))\n            this.$loop.schedule(this.CHANGE_TEXT);\n    };\n\n    this.getShowInvisibles = function() {\n        return this.$textLayer.showInvisibles;\n    };\n\n    this.$showPrintMargin = true;\n    this.setShowPrintMargin = function(showPrintMargin) {\n        this.$showPrintMargin = showPrintMargin;\n        this.$updatePrintMargin();\n    };\n\n    this.getShowPrintMargin = function() {\n        return this.$showPrintMargin;\n    };\n\n    this.$printMarginColumn = 80;\n    this.setPrintMarginColumn = function(showPrintMargin) {\n        this.$printMarginColumn = showPrintMargin;\n        this.$updatePrintMargin();\n    };\n\n    this.getPrintMarginColumn = function() {\n        return this.$printMarginColumn;\n    };\n    \n    this.getShowGutter = function(){\n        return this.showGutter;\n    }\n\n    this.setShowGutter = function(show){\n        if(this.showGutter === show)\n            return;\n        this.$gutter.style.display = show ? \"block\" : \"none\";\n        this.showGutter = show;\n        this.onResize(true);\n    }\n\n    this.$updatePrintMargin = function() {\n        var containerEl\n\n        if (!this.$showPrintMargin && !this.$printMarginEl)\n            return;\n\n        if (!this.$printMarginEl) {\n            containerEl = dom.createElement(\"div\");\n            containerEl.className = \"ace_print_margin_layer\";\n            this.$printMarginEl = dom.createElement(\"div\")\n            this.$printMarginEl.className = \"ace_print_margin\";\n            containerEl.appendChild(this.$printMarginEl);\n            this.content.insertBefore(containerEl, this.$textLayer.element);\n        }\n\n        var style = this.$printMarginEl.style;\n        style.left = ((this.characterWidth * this.$printMarginColumn) + this.$padding * 2) + \"px\";\n        style.visibility = this.$showPrintMargin ? \"visible\" : \"hidden\";\n    };\n\n    this.getContainerElement = function() {\n        return this.container;\n    };\n\n    this.getMouseEventTarget = function() {\n        return this.content;\n    };\n\n    this.getTextAreaContainer = function() {\n        return this.container;\n    };\n\n    this.moveTextAreaToCursor = function(textarea) {\n        // in IE the native cursor always shines through\n        if (useragent.isIE)\n            return;\n\n        var pos = this.$cursorLayer.getPixelPosition();\n        if (!pos)\n            return;\n\n        var bounds = this.content.getBoundingClientRect();\n        var offset = (this.layerConfig && this.layerConfig.offset) || 0;\n\n        textarea.style.left = (bounds.left + pos.left + this.$padding) + \"px\";\n        textarea.style.top = (bounds.top + pos.top - this.scrollTop + offset) + \"px\";\n    };\n\n    this.getFirstVisibleRow = function() {\n        return (this.layerConfig || {}).firstRow || 0;\n    };\n\n    this.getFirstFullyVisibleRow = function(){\n        if (!this.layerConfig)\n            return 0;\n\n        return this.layerConfig.firstRow + (this.layerConfig.offset == 0 ? 0 : 1);\n    }\n\n    this.getLastFullyVisibleRow = function() {\n        if (!this.layerConfig)\n            return 0;\n\n        var flint = Math.floor((this.layerConfig.height + this.layerConfig.offset) / this.layerConfig.lineHeight);\n        return this.layerConfig.firstRow - 1 + flint;\n    }\n\n    this.getLastVisibleRow = function() {\n        return (this.layerConfig || {}).lastRow || 0;\n    };\n\n    this.$padding = null;\n    this.setPadding = function(padding) {\n        this.$padding = padding;\n        this.content.style.padding = \"0 \" + padding + \"px\";\n        this.$loop.schedule(this.CHANGE_FULL);\n        this.$updatePrintMargin();\n    };\n\n    this.getHScrollBarAlwaysVisible = function() {\n        return this.$horizScrollAlwaysVisible;\n    }\n\n    this.setHScrollBarAlwaysVisible = function(alwaysVisible) {\n        if (this.$horizScrollAlwaysVisible != alwaysVisible) {\n            this.$horizScrollAlwaysVisible = alwaysVisible;\n            if (!this.$horizScrollAlwaysVisible || !this.$horizScroll)\n                this.$loop.schedule(this.CHANGE_SCROLL);\n        }\n    }\n\n    this.onScroll = function(e) {\n        this.scrollToY(e.data);\n    };\n\n    this.$updateScrollBar = function() {\n        this.scrollBar.setInnerHeight(this.session.getScreenLength() * this.lineHeight);\n        this.scrollBar.setScrollTop(this.scrollTop);\n    };\n\n    this.$renderChanges = function(changes) {\n        if (!changes || !this.session || !this.$tokenizer)\n            return;\n\n        // text, scrolling and resize changes can cause the view port size to change\n        if (!this.layerConfig ||\n            changes & this.CHANGE_FULL ||\n            changes & this.CHANGE_SIZE ||\n            changes & this.CHANGE_TEXT ||\n            changes & this.CHANGE_LINES ||\n            changes & this.CHANGE_SCROLL\n        )\n            this.$computeLayerConfig();\n\n        // full\n        if (changes & this.CHANGE_FULL) {\n            this.$textLayer.update(this.layerConfig);\n            this.showGutter && this.$gutterLayer.update(this.layerConfig);\n            this.$markerBack.update(this.layerConfig);\n            this.$markerFront.update(this.layerConfig);\n            this.$cursorLayer.update(this.layerConfig);\n            this.$updateScrollBar();\n            this.scrollCursorIntoView();\n            return;\n        }\n\n        // scrolling\n        if (changes & this.CHANGE_SCROLL) {\n            if (changes & this.CHANGE_TEXT || changes & this.CHANGE_LINES)\n                this.$textLayer.update(this.layerConfig);\n            else\n                this.$textLayer.scrollLines(this.layerConfig);\n            this.showGutter && this.$gutterLayer.update(this.layerConfig);\n            this.$markerBack.update(this.layerConfig);\n            this.$markerFront.update(this.layerConfig);\n            this.$cursorLayer.update(this.layerConfig);\n            this.$updateScrollBar();\n            return;\n        }\n\n        if (changes & this.CHANGE_TEXT) {\n            this.$textLayer.update(this.layerConfig);\n            this.showGutter && this.$gutterLayer.update(this.layerConfig);\n        }\n        else if (changes & this.CHANGE_LINES) {\n            this.$updateLines();\n            this.$updateScrollBar();\n            this.showGutter && this.$gutterLayer.update(this.layerConfig);\n        } else if (changes & this.CHANGE_GUTTER) {\n            this.showGutter && this.$gutterLayer.update(this.layerConfig);\n        }\n\n        if (changes & this.CHANGE_CURSOR)\n            this.$cursorLayer.update(this.layerConfig);\n\n        if (changes & (this.CHANGE_MARKER | this.CHANGE_MARKER_FRONT)) {\n            this.$markerFront.update(this.layerConfig);\n        }\n\n        if (changes & (this.CHANGE_MARKER | this.CHANGE_MARKER_BACK)) {\n            this.$markerBack.update(this.layerConfig);\n        }\n\n        if (changes & this.CHANGE_SIZE)\n            this.$updateScrollBar();\n    };\n\n    this.$computeLayerConfig = function() {\n        var session = this.session;\n\n        var offset = this.scrollTop % this.lineHeight;\n        var minHeight = this.$size.scrollerHeight + this.lineHeight;\n\n        var longestLine = this.$getLongestLine();\n        var widthChanged = !this.layerConfig ? true : (this.layerConfig.width != longestLine);\n\n        var horizScroll = this.$horizScrollAlwaysVisible || this.$size.scrollerWidth - longestLine < 0;\n        var horizScrollChanged = this.$horizScroll !== horizScroll;\n        this.$horizScroll = horizScroll;\n        if (horizScrollChanged)\n            this.scroller.style.overflowX = horizScroll ? \"scroll\" : \"hidden\";\n\n        var lineCount = Math.ceil(minHeight / this.lineHeight) - 1;\n        var firstRow = Math.max(0, Math.round((this.scrollTop - offset) / this.lineHeight));\n        var lastRow = firstRow + lineCount;\n\n        // Map lines on the screen to lines in the document.\n        var firstRowScreen, firstRowHeight;\n        var lineHeight = { lineHeight: this.lineHeight };\n        firstRow = session.screenToDocumentRow(firstRow);\n        firstRowScreen = session.documentToScreenRow(firstRow);\n        firstRowHeight = session.getRowHeight(lineHeight, firstRow);\n\n        lastRow = Math.min(session.screenToDocumentRow(lastRow), session.getLength() - 1);\n        minHeight = this.$size.scrollerHeight + session.getRowHeight(lineHeight, lastRow)+\n                                                firstRowHeight;\n\n        offset = this.scrollTop - firstRowScreen * this.lineHeight;\n\n        var layerConfig = this.layerConfig = {\n            width : longestLine,\n            padding : this.$padding,\n            firstRow : firstRow,\n            firstRowScreen: firstRowScreen,\n            lastRow : lastRow,\n            lineHeight : this.lineHeight,\n            characterWidth : this.characterWidth,\n            minHeight : minHeight,\n            offset : offset,\n            height : this.$size.scrollerHeight\n        };\n\n        this.$gutterLayer.element.style.marginTop = (-offset) + \"px\";\n        this.content.style.marginTop = (-offset) + \"px\";\n        this.content.style.width = longestLine + \"px\";\n        this.content.style.height = minHeight + \"px\";\n\n        // Horizontal scrollbar visibility may have changed, which changes\n        // the client height of the scroller\n        if (horizScrollChanged)\n            this.onResize(true);\n    };\n\n    this.$updateLines = function() {\n        var firstRow = this.$changedLines.firstRow;\n        var lastRow = this.$changedLines.lastRow;\n        this.$changedLines = null;\n\n        var layerConfig = this.layerConfig;\n\n        // if the update changes the width of the document do a full redraw\n        if (layerConfig.width != this.$getLongestLine())\n            return this.$textLayer.update(layerConfig);\n\n        if (firstRow > layerConfig.lastRow + 1) { return; }\n        if (lastRow < layerConfig.firstRow) { return; }\n\n        // if the last row is unknown -> redraw everything\n        if (lastRow === Infinity) {\n            this.showGutter && this.$gutterLayer.update(layerConfig);\n            this.$textLayer.update(layerConfig);\n            return;\n        }\n\n        // else update only the changed rows\n        this.$textLayer.updateLines(layerConfig, firstRow, lastRow);\n    };\n\n    this.$getLongestLine = function() {\n        var charCount = this.session.getScreenWidth() + 1;\n        if (this.$textLayer.showInvisibles)\n            charCount += 1;\n\n        return Math.max(this.$size.scrollerWidth - this.$padding * 2, Math.round(charCount * this.characterWidth));\n    };\n\n    this.updateFrontMarkers = function() {\n        this.$markerFront.setMarkers(this.session.getMarkers(true));\n        this.$loop.schedule(this.CHANGE_MARKER_FRONT);\n    };\n\n    this.updateBackMarkers = function() {\n        this.$markerBack.setMarkers(this.session.getMarkers());\n        this.$loop.schedule(this.CHANGE_MARKER_BACK);\n    };\n\n    this.addGutterDecoration = function(row, className){\n        this.$gutterLayer.addGutterDecoration(row, className);\n        this.$loop.schedule(this.CHANGE_GUTTER);\n    }\n\n    this.removeGutterDecoration = function(row, className){\n        this.$gutterLayer.removeGutterDecoration(row, className);\n        this.$loop.schedule(this.CHANGE_GUTTER);\n    }\n\n    this.setBreakpoints = function(rows) {\n        this.$gutterLayer.setBreakpoints(rows);\n        this.$loop.schedule(this.CHANGE_GUTTER);\n    };\n\n    this.setAnnotations = function(annotations) {\n        this.$gutterLayer.setAnnotations(annotations);\n        this.$loop.schedule(this.CHANGE_GUTTER);\n    };\n\n    this.updateCursor = function() {\n        this.$loop.schedule(this.CHANGE_CURSOR);\n    };\n\n    this.hideCursor = function() {\n        this.$cursorLayer.hideCursor();\n    };\n\n    this.showCursor = function() {\n        this.$cursorLayer.showCursor();\n    };\n\n    this.scrollCursorIntoView = function() {\n        // the editor is not visible\n        if (this.$size.scrollerHeight === 0)\n            return;\n            \n        var pos = this.$cursorLayer.getPixelPosition();\n\n        var left = pos.left + this.$padding;\n        var top = pos.top;\n\n        if (this.getScrollTop() > top) {\n            this.scrollToY(top);\n        }\n\n        if (this.getScrollTop() + this.$size.scrollerHeight < top\n                + this.lineHeight) {\n            this.scrollToY(top + this.lineHeight - this.$size.scrollerHeight);\n        }\n\n        if (this.scroller.scrollLeft > left) {\n            this.scrollToX(left);\n        }\n\n        if (this.scroller.scrollLeft + this.$size.scrollerWidth < left + this.characterWidth) {\n\n            if (left + this.characterWidth > this.scroller.scrollWidth)\n                this.$renderChanges(this.CHANGE_SIZE);\n\n            this.scrollToX(Math.round(left + this.characterWidth - this.$size.scrollerWidth));\n        }\n    },\n\n    this.getScrollTop = function() {\n        return this.scrollTop;\n    };\n\n    this.getScrollLeft = function() {\n        return this.scroller.scrollLeft;\n    };\n\n    this.getScrollTopRow = function() {\n        return this.scrollTop / this.lineHeight;\n    };\n\n    this.getScrollBottomRow = function() {\n        return Math.max(0, Math.floor((this.scrollTop + this.$size.scrollerHeight) / this.lineHeight) - 1);\n    }\n\n    this.scrollToRow = function(row) {\n        this.scrollToY(row * this.lineHeight);\n    };\n\n    this.scrollToLine = function(line, center) {\n        var lineHeight = { lineHeight: this.lineHeight };\n        var offset = 0;\n        for (var l = 1; l < line; l++) {\n            offset += this.session.getRowHeight(lineHeight, l-1);\n        }\n            \n        if (center) {\n            offset -= this.$size.scrollerHeight / 2;\n        }\n        this.scrollToY(offset);\n    };\n\n    this.scrollToY = function(scrollTop) {\n        var maxHeight = this.session.getScreenLength() * this.lineHeight - this.$size.scrollerHeight;\n        var scrollTop = Math.max(0, Math.min(maxHeight, scrollTop));\n\n        if (this.scrollTop !== scrollTop) {\n            this.scrollTop = scrollTop;\n            this.$loop.schedule(this.CHANGE_SCROLL);\n        }\n    };\n\n    this.scrollToX = function(scrollLeft) {\n        if (scrollLeft <= this.$padding)\n            scrollLeft = 0;\n\n        this.scroller.scrollLeft = scrollLeft;\n    };\n\n    this.scrollBy = function(deltaX, deltaY) {\n        deltaY && this.scrollToY(this.scrollTop + deltaY);\n        deltaX && this.scrollToX(this.scroller.scrollLeft + deltaX);\n    };\n\n    this.screenToTextCoordinates = function(pageX, pageY) {\n        var canvasPos = this.scroller.getBoundingClientRect();\n\n        var col = Math.round((pageX + this.scroller.scrollLeft - canvasPos.left - this.$padding - dom.getPageScrollLeft())\n                / this.characterWidth);\n        var row = Math.floor((pageY + this.scrollTop - canvasPos.top - dom.getPageScrollTop())\n                / this.lineHeight);\n\n        return this.session.screenToDocumentPosition(row, Math.max(col, 0));\n    };\n\n    this.textToScreenCoordinates = function(row, column) {\n        var canvasPos = this.scroller.getBoundingClientRect();\n        var pos = this.session.documentToScreenPosition(row, column);\n\n        var x = this.$padding + Math.round(pos.column * this.characterWidth);\n        var y = pos.row * this.lineHeight;\n\n        return {\n            pageX: canvasPos.left + x - this.getScrollLeft(),\n            pageY: canvasPos.top + y - this.getScrollTop()\n        }\n    };\n\n    this.visualizeFocus = function() {\n        dom.addCssClass(this.container, \"ace_focus\");\n    };\n\n    this.visualizeBlur = function() {\n        dom.removeCssClass(this.container, \"ace_focus\");\n    };\n\n    this.showComposition = function(position) {\n        if (!this.$composition) {\n            this.$composition = dom.createElement(\"div\");\n            this.$composition.className = \"ace_composition\";\n            this.content.appendChild(this.$composition);\n        }\n\n        this.$composition.innerHTML = \"&#160;\";\n\n        var pos = this.$cursorLayer.getPixelPosition();\n        var style = this.$composition.style;\n        style.top = pos.top + \"px\";\n        style.left = (pos.left + this.$padding) + \"px\";\n        style.height = this.lineHeight + \"px\";\n\n        this.hideCursor();\n    };\n\n    this.setCompositionText = function(text) {\n        dom.setInnerText(this.$composition, text);\n    };\n\n    this.hideComposition = function() {\n        this.showCursor();\n\n        if (!this.$composition)\n            return;\n\n        var style = this.$composition.style;\n        style.top = \"-10000px\";\n        style.left = \"-10000px\";\n    };\n\n    this.setTheme = function(theme) {\n        var _self = this;\n        if (!theme || typeof theme == \"string\") {\n            theme = theme || \"ace/theme/textmate\";\n            require([theme], function(theme) {\n                afterLoad(theme);\n            });\n        } else {\n            afterLoad(theme);\n        }\n\n        var _self = this;\n        function afterLoad(theme) {\n            if (_self.$theme)\n                dom.removeCssClass(_self.container, _self.$theme);\n\n            _self.$theme = theme ? theme.cssClass : null;\n\n            if (_self.$theme)\n                dom.addCssClass(_self.container, _self.$theme);\n\n            // force re-measure of the gutter width\n            if (_self.$size) {\n                _self.$size.width = 0;\n                _self.onResize();\n            }\n        }\n    };\n\n    // Methods allows to add / remove CSS classnames to the editor element.\n    // This feature can be used by plug-ins to provide a visual indication of\n    // a certain mode that editor is in.\n\n    this.setStyle = function setStyle(style) {\n      dom.addCssClass(this.container, style)\n    };\n\n    this.unsetStyle = function unsetStyle(style) {\n      dom.removeCssClass(this.container, style)\n    };\n\n}).call(VirtualRenderer.prototype);\n\nexports.VirtualRenderer = VirtualRenderer;\n});\n/* vim:ts=4:sts=4:sw=4:\n * ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Ajax.org Code Editor (ACE).\n *\n * The Initial Developer of the Original Code is\n * Ajax.org B.V.\n * Portions created by the Initial Developer are Copyright (C) 2010\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *      Fabian Jakobs <fabian AT ajax DOT org>\n *      Julian Viereck <julian.viereck@gmail.com>\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('ace/layer/gutter', function(require, exports, module) {\n\nvar dom = require(\"pilot/dom\");\n\nvar Gutter = function(parentEl) {\n    this.element = dom.createElement(\"div\");\n    this.element.className = \"ace_layer ace_gutter-layer\";\n    parentEl.appendChild(this.element);\n\n    this.$breakpoints = [];\n    this.$annotations = [];\n    this.$decorations = [];\n};\n\n(function() {\n\n    this.setSession = function(session) {\n        this.session = session;\n    };\n\n    this.addGutterDecoration = function(row, className){\n        if (!this.$decorations[row])\n            this.$decorations[row] = \"\";\n        this.$decorations[row] += \" ace_\" + className;\n    }\n\n    this.removeGutterDecoration = function(row, className){\n        this.$decorations[row] = this.$decorations[row].replace(\" ace_\" + className, \"\");\n    };\n\n    this.setBreakpoints = function(rows) {\n        this.$breakpoints = rows.concat();\n    };\n\n    this.setAnnotations = function(annotations) {\n        // iterate over sparse array\n        this.$annotations = [];        \n        for (var row in annotations) if (annotations.hasOwnProperty(row)) {\n            var rowAnnotations = annotations[row];\n            if (!rowAnnotations)\n                continue;\n                \n            var rowInfo = this.$annotations[row] = {\n                text: []\n            };\n            for (var i=0; i<rowAnnotations.length; i++) {\n                var annotation = rowAnnotations[i];\n                rowInfo.text.push(annotation.text.replace(/\"/g, \"&quot;\").replace(/'/g, \"&#8217;\").replace(/</, \"&lt;\"));\n                var type = annotation.type;\n                if (type == \"error\")\n                    rowInfo.className = \"ace_error\";\n                else if (type == \"warning\" && rowInfo.className != \"ace_error\")\n                    rowInfo.className = \"ace_warning\";\n                else if (type == \"info\" && (!rowInfo.className))\n                    rowInfo.className = \"ace_info\";\n            }\n        }\n    };\n\n    this.update = function(config) {\n        this.$config = config;\n\n        var html = [];\n        for ( var i = config.firstRow; i <= config.lastRow; i++) {\n            var annotation = this.$annotations[i] || {\n                className: \"\",\n                text: []\n            };\n            html.push(\"<div class='ace_gutter-cell\",\n                this.$decorations[i] || \"\",\n                this.$breakpoints[i] ? \" ace_breakpoint \" : \" \",\n                annotation.className,\n                \"' title='\", annotation.text.join(\"\\n\"),\n                \"' style='height:\", this.session.getRowHeight(config, i), \"px;'>\", (i+1), \"</div>\");\n        }\n\t\tthis.element = dom.setInnerHtml(this.element, html.join(\"\"));\n        this.element.style.height = config.minHeight + \"px\";\n    };\n\n}).call(Gutter.prototype);\n\nexports.Gutter = Gutter;\n\n});\n/* vim:ts=4:sts=4:sw=4:\n * ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Ajax.org Code Editor (ACE).\n *\n * The Initial Developer of the Original Code is\n * Ajax.org B.V.\n * Portions created by the Initial Developer are Copyright (C) 2010\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *      Fabian Jakobs <fabian AT ajax DOT org>\n *      Julian Viereck <julian.viereck@gmail.com>\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('ace/layer/marker', function(require, exports, module) {\n\nvar Range = require(\"ace/range\").Range;\nvar dom = require(\"pilot/dom\");\n\nvar Marker = function(parentEl) {\n    this.element = dom.createElement(\"div\");\n    this.element.className = \"ace_layer ace_marker-layer\";\n    parentEl.appendChild(this.element);\n};\n\n(function() {\n\n    this.setSession = function(session) {\n        this.session = session;\n    };\n    \n    this.setMarkers = function(markers) {\n        this.markers = markers;\n    };\n\n    this.update = function(config) {\n        var config = config || this.config;\n        if (!config)\n            return;\n\n        this.config = config;\n\n        var html = [];        \n        for ( var key in this.markers) {\n            var marker = this.markers[key];\n\n            var range = marker.range.clipRows(config.firstRow, config.lastRow);\n            if (range.isEmpty()) continue;\n\n            range = range.toScreenRange(this.session);\n\n            if (marker.renderer) {\n                var top = this.$getTop(range.start.row, config);\n                var left = Math.round(range.start.column * config.characterWidth);        \n                marker.renderer(html, range, left, top, config);\n            }\n            else if (range.isMultiLine()) {\n                if (marker.type == \"text\") {\n                    this.drawTextMarker(html, range, marker.clazz, config);\n                } else {\n                    this.drawMultiLineMarker(html, range, marker.clazz, config);\n                }\n            }\n            else {\n                this.drawSingleLineMarker(html, range, marker.clazz, config);\n            }\n        }\n        this.element = dom.setInnerHtml(this.element, html.join(\"\"));\n    };\n\n    this.$getTop = function(row, layerConfig) {\n        return (row - layerConfig.firstRowScreen) * layerConfig.lineHeight;\n    };\n\n    this.drawTextMarker = function(stringBuilder, range, clazz, layerConfig) {\n        // selection start\n        var row = range.start.row;\n\n        var lineRange = new Range(row, range.start.column,\n                                  row, this.session.getScreenLastRowColumn(row));\n        this.drawSingleLineMarker(stringBuilder, lineRange, clazz, layerConfig, 1);\n\n        // selection end\n        var row = range.end.row;\n        var lineRange = new Range(row, 0, row, range.end.column);\n        this.drawSingleLineMarker(stringBuilder, lineRange, clazz, layerConfig);\n\n        for (var row = range.start.row + 1; row < range.end.row; row++) {\n            lineRange.start.row = row;\n            lineRange.end.row = row;\n            lineRange.end.column = this.session.getScreenLastRowColumn(row);\n            this.drawSingleLineMarker(stringBuilder, lineRange, clazz, layerConfig, 1);\n        }\n    };\n\n    this.drawMultiLineMarker = function(stringBuilder, range, clazz, layerConfig) {\n        // from selection start to the end of the line\n        var height = layerConfig.lineHeight;\n        var width = Math.round(layerConfig.width - (range.start.column * layerConfig.characterWidth));\n        var top = this.$getTop(range.start.row, layerConfig);\n        var left = Math.round(range.start.column * layerConfig.characterWidth);\n\n        stringBuilder.push(\n            \"<div class='\", clazz, \"' style='\",\n            \"height:\", height, \"px;\",\n            \"width:\", width, \"px;\",\n            \"top:\", top, \"px;\",\n            \"left:\", left, \"px;'></div>\"\n        );\n\n        // from start of the last line to the selection end\n        var top = this.$getTop(range.end.row, layerConfig);\n        var width = Math.round(range.end.column * layerConfig.characterWidth);\n\n        stringBuilder.push(\n            \"<div class='\", clazz, \"' style='\",\n            \"height:\", height, \"px;\",\n            \"top:\", top, \"px;\",\n            \"width:\", width, \"px;'></div>\"\n        );\n\n        // all the complete lines\n        var height = (range.end.row - range.start.row - 1) * layerConfig.lineHeight;\n        if (height < 0)\n            return;\n        var top = this.$getTop(range.start.row + 1, layerConfig);\n\n        stringBuilder.push(\n            \"<div class='\", clazz, \"' style='\",\n            \"height:\", height, \"px;\",\n            \"width:\", layerConfig.width, \"px;\",\n            \"top:\", top, \"px;'></div>\"\n        );\n    };\n\n    this.drawSingleLineMarker = function(stringBuilder, range, clazz, layerConfig, extraLength) {\n        var height = layerConfig.lineHeight;\n        var width = Math.round((range.end.column + (extraLength || 0) - range.start.column) * layerConfig.characterWidth);\n        var top = this.$getTop(range.start.row, layerConfig);\n        var left = Math.round(range.start.column * layerConfig.characterWidth);\n\n        stringBuilder.push(\n            \"<div class='\", clazz, \"' style='\",\n            \"height:\", height, \"px;\",\n            \"width:\", width, \"px;\",\n            \"top:\", top, \"px;\",\n            \"left:\", left,\"px;'></div>\"\n        );\n    };\n\n}).call(Marker.prototype);\n\nexports.Marker = Marker;\n\n});\n/* vim:ts=4:sts=4:sw=4:\n * ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Ajax.org Code Editor (ACE).\n *\n * The Initial Developer of the Original Code is\n * Ajax.org B.V.\n * Portions created by the Initial Developer are Copyright (C) 2010\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *      Fabian Jakobs <fabian AT ajax DOT org>\n *      Julian Viereck <julian.viereck@gmail.com>\n *      Mihai Sucan <mihai.sucan@gmail.com>\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('ace/layer/text', function(require, exports, module) {\n\nvar oop = require(\"pilot/oop\");\nvar dom = require(\"pilot/dom\");\nvar lang = require(\"pilot/lang\");\nvar EventEmitter = require(\"pilot/event_emitter\").EventEmitter;\n\nvar Text = function(parentEl) {\n    this.element = dom.createElement(\"div\");\n    this.element.className = \"ace_layer ace_text-layer\";\n    parentEl.appendChild(this.element);\n\n    this.$characterSize = this.$measureSizes() || {width: 0, height: 0};\n    this.$pollSizeChanges();\n};\n\n(function() {\n\n    oop.implement(this, EventEmitter);\n\n    this.EOF_CHAR = \"&para;\";\n    this.EOL_CHAR = \"&not;\";\n    this.TAB_CHAR = \"&rarr;\";\n    this.SPACE_CHAR = \"&middot;\";\n\n    this.setTokenizer = function(tokenizer) {\n        this.tokenizer = tokenizer;\n    };\n\n    this.getLineHeight = function() {\n        return this.$characterSize.height || 1;\n    };\n\n    this.getCharacterWidth = function() {\n        return this.$characterSize.width || 1;\n    };\n\n    this.checkForSizeChanges = function() {\n        var size = this.$measureSizes();\n        if (size && (this.$characterSize.width !== size.width || this.$characterSize.height !== size.height)) {\n            this.$characterSize = size;\n            this._dispatchEvent(\"changeCharaterSize\", {data: size});\n        }\n    };\n\n    this.$pollSizeChanges = function() {\n        var self = this;\n        setInterval(function() {\n            self.checkForSizeChanges();\n        }, 500);\n    };\n\n    this.$fontStyles = {\n        fontFamily : 1,\n        fontSize : 1,\n        fontWeight : 1,\n        fontStyle : 1,\n        lineHeight : 1\n    };\n\n    this.$measureSizes = function() {\n        var n = 1000;\n        if (!this.$measureNode) {\n            var measureNode = this.$measureNode = dom.createElement(\"div\");\n            var style = measureNode.style;\n\n            style.width = style.height = \"auto\";\n            style.left = style.top = (-n * 40)  + \"px\";\n\n            style.visibility = \"hidden\";\n            style.position = \"absolute\";\n            style.overflow = \"visible\";\n            style.whiteSpace = \"nowrap\";\n\n            // in FF 3.6 monospace fonts can have a fixed sub pixel width.\n            // that's why we have to measure many characters\n            // Note: characterWidth can be a float!\n            measureNode.innerHTML = lang.stringRepeat(\"Xy\", n);\n\n            if (document.body) {\n                document.body.appendChild(measureNode);\n            } else {\n                var container = this.element.parentNode;\n                while (!dom.hasCssClass(container, \"ace_editor\"))\n                    container = container.parentNode;\n                container.appendChild(measureNode);\n            }\n\n        }\n\n        var style = this.$measureNode.style;\n        for (var prop in this.$fontStyles) {\n            var value = dom.computedStyle(this.element, prop);\n            style[prop] = value;\n        }\n\n        var size = {\n            height: this.$measureNode.offsetHeight,\n            width: this.$measureNode.offsetWidth / (n * 2)\n        };\n\n        // Size and width can be null if the editor is not visible or\n        // detached from the document\n        if (size.width == 0 && size.height == 0)\n            return null;\n\n        return size;\n    };\n\n    this.setSession = function(session) {\n        this.session = session;\n    };\n\n    this.showInvisibles = false;\n    this.setShowInvisibles = function(showInvisibles) {\n        if (this.showInvisibles == showInvisibles)\n            return false;\n\n        this.showInvisibles = showInvisibles;\n        return true;\n    };\n\n    this.$computeTabString = function() {\n        var tabSize = this.session.getTabSize();\n        if (this.showInvisibles) {\n            var halfTab = (tabSize) / 2;\n            this.$tabString = \"<span class='ace_invisible'>\"\n                + new Array(Math.floor(halfTab)).join(\"&#160;\")\n                + this.TAB_CHAR\n                + new Array(Math.ceil(halfTab)+1).join(\"&#160;\")\n                + \"</span>\";\n        } else {\n            this.$tabString = new Array(tabSize+1).join(\"&#160;\");\n        }\n    };\n\n    this.updateLines = function(config, firstRow, lastRow) {\n        this.$computeTabString();\n        // Due to wrap line changes there can be new lines if e.g.\n        // the line to updated wrapped in the meantime.\n        if (this.config.lastRow != config.lastRow ||\n            this.config.firstRow != config.firstRow) {\n            this.scrollLines(config);\n        }\n        this.config = config;\n\n        var first = Math.max(firstRow, config.firstRow);\n        var last = Math.min(lastRow, config.lastRow);\n\n        var lineElements = this.element.childNodes;\n        var tokens = this.tokenizer.getTokens(first, last);\n        for (var i=first; i<=last; i++) {\n            var lineElement = lineElements[i - config.firstRow];\n            if (!lineElement)\n                continue;\n\n            var html = [];\n            this.$renderLine(html, i, tokens[i-first].tokens);\n            lineElement = dom.setInnerHtml(lineElement, html.join(\"\"));\n            lineElement.style.height =\n                    this.session.getRowHeight(config, i) + \"px\";\n        }\n    };\n\n    this.scrollLines = function(config) {\n        this.$computeTabString();\n        var oldConfig = this.config;\n        this.config = config;\n\n        if (!oldConfig || oldConfig.lastRow < config.firstRow)\n            return this.update(config);\n\n        if (config.lastRow < oldConfig.firstRow)\n            return this.update(config);\n\n        var el = this.element;\n\n        if (oldConfig.firstRow < config.firstRow)\n            for (var row=oldConfig.firstRow; row<config.firstRow; row++)\n                el.removeChild(el.firstChild);\n\n        if (oldConfig.lastRow > config.lastRow)\n            for (var row=config.lastRow+1; row<=oldConfig.lastRow; row++)\n                el.removeChild(el.lastChild);\n\n        if (config.firstRow < oldConfig.firstRow) {\n            var fragment = this.$renderLinesFragment(config, config.firstRow, oldConfig.firstRow - 1);\n            if (el.firstChild)\n                el.insertBefore(fragment, el.firstChild);\n            else\n                el.appendChild(fragment);\n        }\n\n        if (config.lastRow > oldConfig.lastRow) {\n            var fragment = this.$renderLinesFragment(config, oldConfig.lastRow + 1, config.lastRow);\n            el.appendChild(fragment);\n        }\n    };\n\n    this.$renderLinesFragment = function(config, firstRow, lastRow) {\n        var fragment = document.createDocumentFragment();\n        var tokens = this.tokenizer.getTokens(firstRow, lastRow);\n        for (var row=firstRow; row<=lastRow; row++) {\n            var lineEl = dom.createElement(\"div\");\n            lineEl.className = \"ace_line\";\n            var style = lineEl.style;\n            style.height = this.session.getRowHeight(config, row) + \"px\";\n            style.width = config.width + \"px\";\n\n            var html = [];\n            if (tokens.length > row-firstRow)\n                this.$renderLine(html, row, tokens[row-firstRow].tokens);\n            // don't use setInnerHtml since we are working with an empty DIV\n            lineEl.innerHTML = html.join(\"\");\n            fragment.appendChild(lineEl);\n        }\n        return fragment;\n    };\n\n    this.update = function(config) {\n        this.$computeTabString();\n        this.config = config;\n\n        var html = [];\n        var tokens = this.tokenizer.getTokens(config.firstRow, config.lastRow)\n        var fragment = this.$renderLinesFragment(config, config.firstRow, config.lastRow);\n\n        // Clear the current content of the element and add the rendered fragment.\n        this.element.innerHTML =  \"\";\n        this.element.appendChild(fragment);\n    };\n\n    this.$textToken = {\n        \"text\": true,\n        \"rparen\": true,\n        \"lparen\": true\n    };\n\n    this.$renderLine = function(stringBuilder, row, tokens) {\n        if (this.showInvisibles) {\n            var self = this;\n            var spaceRe = /( +)|([\\v\\f \\u00a0\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u200b\\u2028\\u2029\\u3000])/g;\n            var spaceReplace = function(space) {\n                if (space.charCodeAt(0) == 32)\n                    return new Array(space.length+1).join(\"&#160;\");\n                else {\n                    var space = new Array(space.length+1).join(self.SPACE_CHAR);\n                    return \"<span class='ace_invisible'>\" + space + \"</span>\";\n                }\n\n            };\n        }\n        else {\n            var spaceRe = /[\\v\\f \\u00a0\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u200b\\u2028\\u2029\\u3000]/g;\n            var spaceReplace = \"&#160;\";\n        }\n\n        var _self = this;\n        var characterWidth = this.config.characterWidth;\n        function addToken(token, value) {\n            var output = value\n                .replace(/&/g, \"&amp;\")\n                .replace(/</g, \"&lt;\")\n                .replace(spaceRe, spaceReplace)\n                .replace(/\\t/g, _self.$tabString)\n                .replace(/[\\u3040-\\u309F]|[\\u30A0-\\u30FF]|[\\u4E00-\\u9FFF\\uF900-\\uFAFF\\u3400-\\u4DBF]/g, function(c) {\n                    return \"<span class='ace_cjk' style='width:\" + (characterWidth * 2) + \"px'>\" + c + \"</span>\"\n                });\n\n            if (!_self.$textToken[token.type]) {\n                var classes = \"ace_\" + token.type.replace(/\\./g, \" ace_\");\n                stringBuilder.push(\"<span class='\", classes, \"'>\", output, \"</span>\");\n            }\n            else {\n                stringBuilder.push(output);\n            }\n        }\n\n        var splits = this.session.getRowSplitData(row);\n        var chars = 0, split = 0, splitChars;\n\n        if (!splits || splits.length == 0) {\n            splitChars = Number.MAX_VALUE;\n        } else {\n            splitChars = splits[0];\n        }\n\n        stringBuilder.push(\"<div style='height:\",\n            this.config.lineHeight, \"px\",\n            \"'>\");\n        for (var i = 0; i < tokens.length; i++) {\n            var token = tokens[i];\n            var value = token.value;\n\n            if (chars + value.length < splitChars) {\n                addToken(token, value);\n                chars += value.length;\n            } else {\n                while (chars + value.length >= splitChars) {\n                    addToken(token, value.substring(0, splitChars - chars));\n                    value = value.substring(splitChars - chars);\n                    chars = splitChars;\n                    stringBuilder.push(\"</div>\",\n                        \"<div style='height:\",\n                        this.config.lineHeight, \"px\",\n                        \"'>\");\n                    split ++;\n                    splitChars = splits[split] || Number.MAX_VALUE;\n                }\n                if (value.length != 0) {\n                    chars += value.length;\n                    addToken(token, value);\n                }\n            }\n        };\n\n        if (this.showInvisibles) {\n            if (row !== this.session.getLength() - 1) {\n                stringBuilder.push(\"<span class='ace_invisible'>\" + this.EOL_CHAR + \"</span>\");\n            } else {\n                stringBuilder.push(\"<span class='ace_invisible'>\" + this.EOF_CHAR + \"</span>\");\n            }\n        }\n        stringBuilder.push(\"</div>\");\n    };\n\n}).call(Text.prototype);\n\nexports.Text = Text;\n\n});\n/* vim:ts=4:sts=4:sw=4:\n * ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Ajax.org Code Editor (ACE).\n *\n * The Initial Developer of the Original Code is\n * Ajax.org B.V.\n * Portions created by the Initial Developer are Copyright (C) 2010\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *      Fabian Jakobs <fabian AT ajax DOT org>\n *      Julian Viereck <julian.viereck@gmail.com>\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('ace/layer/cursor', function(require, exports, module) {\n\nvar dom = require(\"pilot/dom\");\n\nvar Cursor = function(parentEl) {\n    this.element = dom.createElement(\"div\");\n    this.element.className = \"ace_layer ace_cursor-layer\";\n    parentEl.appendChild(this.element);\n\n    this.cursor = dom.createElement(\"div\");\n    this.cursor.className = \"ace_cursor\";\n\n    this.isVisible = false;\n};\n\n(function() {\n\n    this.setSession = function(session) {\n        this.session = session;\n    };\n\n    this.hideCursor = function() {\n        this.isVisible = false;\n        if (this.cursor.parentNode) {\n            this.cursor.parentNode.removeChild(this.cursor);\n        }\n        clearInterval(this.blinkId);\n    };\n\n    this.showCursor = function() {\n        this.isVisible = true;\n        this.element.appendChild(this.cursor);\n\n        var cursor = this.cursor;\n        cursor.style.visibility = \"visible\";\n        this.restartTimer();\n    };\n\n    this.restartTimer = function() {\n        clearInterval(this.blinkId);\n        if (!this.isVisible) {\n            return;\n        }\n\n        var cursor = this.cursor;\n        this.blinkId = setInterval(function() {\n            cursor.style.visibility = \"hidden\";\n            setTimeout(function() {\n                cursor.style.visibility = \"visible\";\n            }, 400);\n        }, 1000);\n    };\n\n    this.getPixelPosition = function(onScreen) {\n        if (!this.config || !this.session) {\n            return {\n                left : 0,\n                top : 0\n            };\n        }\n\n        var position = this.session.selection.getCursor();\n        var pos = this.session.documentToScreenPosition(position);\n        var cursorLeft = Math.round(pos.column * this.config.characterWidth);\n        var cursorTop = (pos.row - (onScreen ? this.config.firstRowScreen : 0)) *\n            this.config.lineHeight;\n\n        return {\n            left : cursorLeft,\n            top : cursorTop\n        };\n    };\n\n    this.update = function(config) {\n        this.config = config;\n\n        this.pixelPos = this.getPixelPosition(true);\n\n        this.cursor.style.left = this.pixelPos.left + \"px\";\n        this.cursor.style.top =  this.pixelPos.top + \"px\";\n        this.cursor.style.width = config.characterWidth + \"px\";\n        this.cursor.style.height = config.lineHeight + \"px\";\n\n        if (this.isVisible) {\n            this.element.appendChild(this.cursor);\n        }\n        \n        if (this.session.getOverwrite()) {\n            dom.addCssClass(this.cursor, \"ace_overwrite\");\n        } else {\n            dom.removeCssClass(this.cursor, \"ace_overwrite\");\n        }\n        \n        this.restartTimer();\n    };\n\n}).call(Cursor.prototype);\n\nexports.Cursor = Cursor;\n\n});\n/* ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Ajax.org Code Editor (ACE).\n *\n * The Initial Developer of the Original Code is\n * Ajax.org B.V.\n * Portions created by the Initial Developer are Copyright (C) 2010\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *      Fabian Jakobs <fabian AT ajax DOT org>\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('ace/scrollbar', function(require, exports, module) {\n\nvar oop = require(\"pilot/oop\");\nvar dom = require(\"pilot/dom\");\nvar event = require(\"pilot/event\");\nvar EventEmitter = require(\"pilot/event_emitter\").EventEmitter;\n\nvar ScrollBar = function(parent) {\n    this.element = dom.createElement(\"div\");\n    this.element.className = \"ace_sb\";\n\n    this.inner = dom.createElement(\"div\");\n    this.element.appendChild(this.inner);\n\n    parent.appendChild(this.element);\n\n    this.width = dom.scrollbarWidth();\n    this.element.style.width = this.width + \"px\";\n\n    event.addListener(this.element, \"scroll\", this.onScroll.bind(this));\n};\n\n(function() {\n    oop.implement(this, EventEmitter);\n\n    this.onScroll = function() {\n        this._dispatchEvent(\"scroll\", {data: this.element.scrollTop});\n    };\n\n    this.getWidth = function() {\n        return this.width;\n    };\n\n    this.setHeight = function(height) {\n        this.element.style.height = height + \"px\";\n    };\n\n    this.setInnerHeight = function(height) {\n        this.inner.style.height = height + \"px\";\n    };\n\n    this.setScrollTop = function(scrollTop) {\n        this.element.scrollTop = scrollTop;\n    };\n\n}).call(ScrollBar.prototype);\n\nexports.ScrollBar = ScrollBar;\n});\n/* ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Ajax.org Code Editor (ACE).\n *\n * The Initial Developer of the Original Code is\n * Ajax.org B.V.\n * Portions created by the Initial Developer are Copyright (C) 2010\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *      Fabian Jakobs <fabian AT ajax DOT org>\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('ace/renderloop', function(require, exports, module) {\n\nvar event = require(\"pilot/event\");\n\nvar RenderLoop = function(onRender) {\n    this.onRender = onRender;\n    this.pending = false;\n    this.changes = 0;\n};\n\n(function() {\n\n    this.schedule = function(change) {\n        //this.onRender(change);\n        //return;\n        this.changes = this.changes | change;\n        if (!this.pending) {\n            this.pending = true;\n            var _self = this;\n            this.setTimeoutZero(function() {\n                _self.pending = false;\n                var changes = _self.changes;\n                _self.changes = 0;\n                _self.onRender(changes);\n            })\n        }\n    };\n\n    if (window.postMessage) {\n\n        this.messageName = \"zero-timeout-message\";\n\n        this.setTimeoutZero = function(callback) {\n            if (!this.attached) {\n                var _self = this;\n                event.addListener(window, \"message\", function(e) {\n                    if (_self.callback && e.data == _self.messageName) {\n                        event.stopPropagation(e);\n                        _self.callback();\n                    }\n                });\n                this.attached = true;\n            }\n            this.callback = callback;\n            window.postMessage(this.messageName, \"*\");\n        }\n\n    } else {\n\n        this.setTimeoutZero = function(callback) {\n            setTimeout(callback, 0);\n        }\n    }\n\n}).call(RenderLoop.prototype);\n\nexports.RenderLoop = RenderLoop;\n});\ndefine(\"text/ace/css/editor.css\", \".ace_editor {\" +\n  \"    position: absolute;\" +\n  \"    overflow: hidden;\" +\n  \"\" +\n  \"    font-family: \\\"Menlo\\\", \\\"Monaco\\\", \\\"Courier New\\\", monospace;\" +\n  \"    font-size: 12px;  \" +\n  \"}\" +\n  \"\" +\n  \".ace_scroller {\" +\n  \"    position: absolute;\" +\n  \"    overflow-x: scroll;\" +\n  \"    overflow-y: hidden;     \" +\n  \"}\" +\n  \"\" +\n  \".ace_content {\" +\n  \"    position: absolute;\" +\n  \"    box-sizing: border-box;\" +\n  \"    -moz-box-sizing: border-box;\" +\n  \"    -webkit-box-sizing: border-box;\" +\n  \"}\" +\n  \"\" +\n  \".ace_composition {\" +\n  \"    position: absolute;\" +\n  \"    background: #555;\" +\n  \"    color: #DDD;\" +\n  \"    z-index: 4;\" +\n  \"}\" +\n  \"\" +\n  \".ace_gutter {\" +\n  \"    position: absolute;\" +\n  \"    overflow-x: hidden;\" +\n  \"    overflow-y: hidden;\" +\n  \"    height: 100%;\" +\n  \"}\" +\n  \"\" +\n  \".ace_gutter-cell.ace_error {\" +\n  \"    background-image: url(\\\"data:image/gif,GIF89a%10%00%10%00%D5%00%00%F5or%F5%87%88%F5nr%F4ns%EBmq%F5z%7F%DDJT%DEKS%DFOW%F1Yc%F2ah%CE(7%CE)8%D18E%DD%40M%F2KZ%EBU%60%F4%60m%DCir%C8%16(%C8%19*%CE%255%F1%3FR%F1%3FS%E6%AB%B5%CA%5DI%CEn%5E%F7%A2%9A%C9G%3E%E0a%5B%F7%89%85%F5yy%F6%82%80%ED%82%80%FF%BF%BF%E3%C4%C4%FF%FF%FF%FF%FF%FF%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00!%F9%04%01%00%00%25%00%2C%00%00%00%00%10%00%10%00%00%06p%C0%92pH%2C%1A%8F%C8%D2H%93%E1d4%23%E4%88%D3%09mB%1DN%B48%F5%90%40%60%92G%5B%94%20%3E%22%D2%87%24%FA%20%24%C5%06A%00%20%B1%07%02B%A38%89X.v%17%82%11%13q%10%0Fi%24%0F%8B%10%7BD%12%0Ei%09%92%09%0EpD%18%15%24%0A%9Ci%05%0C%18F%18%0B%07%04%01%04%06%A0H%18%12%0D%14%0D%12%A1I%B3%B4%B5IA%00%3B\\\");\" +\n  \"    background-repeat: no-repeat;\" +\n  \"    background-position: 4px center;\" +\n  \"}\" +\n  \"\" +\n  \".ace_gutter-cell.ace_warning {\" +\n  \"    background-image: url(\\\"data:image/gif,GIF89a%10%00%10%00%D5%00%00%FF%DBr%FF%DE%81%FF%E2%8D%FF%E2%8F%FF%E4%96%FF%E3%97%FF%E5%9D%FF%E6%9E%FF%EE%C1%FF%C8Z%FF%CDk%FF%D0s%FF%D4%81%FF%D5%82%FF%D5%83%FF%DC%97%FF%DE%9D%FF%E7%B8%FF%CCl%7BQ%13%80U%15%82W%16%81U%16%89%5B%18%87%5B%18%8C%5E%1A%94d%1D%C5%83-%C9%87%2F%C6%84.%C6%85.%CD%8B2%C9%871%CB%8A3%CD%8B5%DC%98%3F%DF%9BB%E0%9CC%E1%A5U%CB%871%CF%8B5%D1%8D6%DB%97%40%DF%9AB%DD%99B%E3%B0p%E7%CC%AE%FF%FF%FF%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00!%F9%04%01%00%00%2F%00%2C%00%00%00%00%10%00%10%00%00%06a%C0%97pH%2C%1A%8FH%A1%ABTr%25%87%2B%04%82%F4%7C%B9X%91%08%CB%99%1C!%26%13%84*iJ9(%15G%CA%84%14%01%1A%97%0C%03%80%3A%9A%3E%81%84%3E%11%08%B1%8B%20%02%12%0F%18%1A%0F%0A%03'F%1C%04%0B%10%16%18%10%0B%05%1CF%1D-%06%07%9A%9A-%1EG%1B%A0%A1%A0U%A4%A5%A6BA%00%3B\\\");\" +\n  \"    background-repeat: no-repeat;\" +\n  \"    background-position: 4px center;\" +\n  \"}\" +\n  \"\" +\n  \".ace_editor .ace_sb {\" +\n  \"    position: absolute;\" +\n  \"    overflow-x: hidden;\" +\n  \"    overflow-y: scroll;\" +\n  \"    right: 0;\" +\n  \"}\" +\n  \"\" +\n  \".ace_editor .ace_sb div {\" +\n  \"    position: absolute;\" +\n  \"    width: 1px;\" +\n  \"    left: 0;\" +\n  \"}\" +\n  \"\" +\n  \".ace_editor .ace_print_margin_layer {\" +\n  \"    z-index: 0;\" +\n  \"    position: absolute;\" +\n  \"    overflow: hidden;\" +\n  \"    margin: 0;\" +\n  \"    left: 0;\" +\n  \"    height: 100%;\" +\n  \"    width: 100%;\" +\n  \"}\" +\n  \"\" +\n  \".ace_editor .ace_print_margin {\" +\n  \"    position: absolute;\" +\n  \"    height: 100%;\" +\n  \"}\" +\n  \"\" +\n  \".ace_editor textarea {\" +\n  \"    position: fixed;\" +\n  \"    z-index: -1;\" +\n  \"    width: 10px;\" +\n  \"    height: 30px;\" +\n  \"    opacity: 0;\" +\n  \"    background: transparent;\" +\n  \"    appearance: none;\" +\n  \"    border: none;\" +\n  \"    resize: none;\" +\n  \"    outline: none;\" +\n  \"    overflow: hidden;\" +\n  \"}\" +\n  \"\" +\n  \".ace_layer {\" +\n  \"    z-index: 1;\" +\n  \"    position: absolute;\" +\n  \"    overflow: hidden;  \" +\n  \"    white-space: nowrap;\" +\n  \"    height: 100%;\" +\n  \"    width: 100%;\" +\n  \"}\" +\n  \"\" +\n  \".ace_text-layer {\" +\n  \"    font-family: Monaco, \\\"Courier New\\\", monospace;\" +\n  \"    color: black;\" +\n  \"}\" +\n  \"\" +\n  \".ace_cjk {\" +\n  \"    display: inline-block;\" +\n  \"    text-align: center;\" +\n  \"}\" +\n  \"\" +\n  \".ace_cursor-layer {\" +\n  \"    z-index: 4;\" +\n  \"    cursor: text;\" +\n  \"    pointer-events: none;\" +\n  \"}\" +\n  \"\" +\n  \".ace_cursor {\" +\n  \"    z-index: 4;\" +\n  \"    position: absolute;\" +\n  \"}\" +\n  \"\" +\n  \".ace_line {\" +\n  \"    white-space: nowrap;\" +\n  \"}\" +\n  \"\" +\n  \".ace_marker-layer {\" +\n  \"    cursor: text;\" +\n  \"}\" +\n  \"\" +\n  \".ace_marker-layer .ace_step {\" +\n  \"    position: absolute;\" +\n  \"    z-index: 3;\" +\n  \"}\" +\n  \"\" +\n  \".ace_marker-layer .ace_selection {\" +\n  \"    position: absolute;\" +\n  \"    z-index: 4;\" +\n  \"}\" +\n  \"\" +\n  \".ace_marker-layer .ace_bracket {\" +\n  \"    position: absolute;\" +\n  \"    z-index: 5;\" +\n  \"}\" +\n  \"\" +\n  \".ace_marker-layer .ace_active_line {\" +\n  \"    position: absolute;\" +\n  \"    z-index: 2;\" +\n  \"}\" +\n  \"\" +\n  \".ace_marker-layer .ace_selected_word {\" +\n  \"    position: absolute;\" +\n  \"    z-index: 6;\" +\n  \"    box-sizing: border-box;\" +\n  \"    -moz-box-sizing: border-box;\" +\n  \"    -webkit-box-sizing: border-box;\" +\n  \"}\" +\n  \"\" +\n  \".ace_dragging .ace_marker-layer, .ace_dragging .ace_text-layer {\" +\n  \"  cursor: move;\" +\n  \"}\" +\n  \"\");\n\ndefine(\"text/styles.css\", \"html {\" +\n  \"    height: 100%;\" +\n  \"    overflow: hidden;\" +\n  \"}\" +\n  \"\" +\n  \"body {\" +\n  \"    overflow: hidden;\" +\n  \"    margin: 0;\" +\n  \"    padding: 0;\" +\n  \"    height: 100%;\" +\n  \"    width: 100%;\" +\n  \"    font-family: Arial, Helvetica, sans-serif, Tahoma, Verdana, sans-serif;\" +\n  \"    font-size: 12px;\" +\n  \"    background: rgb(14, 98, 165);\" +\n  \"    color: white;\" +\n  \"}\" +\n  \"\" +\n  \"#editor {\" +\n  \"    position: absolute;\" +\n  \"    top: 60px;\" +\n  \"    left: 0px;\" +\n  \"    background: white;\" +\n  \"}\" +\n  \"\" +\n  \"#controls {\" +\n  \"    width: 100%;\" +\n  \"}\" +\n  \"\" +\n  \"#cockpitInput {\" +\n  \"    position: absolute;\" +\n  \"    width: 100%;\" +\n  \"    bottom: 0;\" +\n  \"\" +\n  \"    border: none; outline: none;\" +\n  \"    font-family: consolas, courier, monospace;\" +\n  \"    font-size: 120%;\" +\n  \"}\" +\n  \"\" +\n  \"#cockpitOutput {\" +\n  \"    padding: 10px;\" +\n  \"    margin: 0 15px;\" +\n  \"    border: 1px solid #AAA;\" +\n  \"    -moz-border-radius-topleft: 10px;\" +\n  \"    -moz-border-radius-topright: 10px;\" +\n  \"    border-top-left-radius: 4px; border-top-right-radius: 4px;\" +\n  \"    background: #DDD; color: #000;\" +\n  \"}\");\n\n/* ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Mozilla Skywriter.\n *\n * The Initial Developer of the Original Code is\n * Mozilla.\n * Portions created by the Initial Developer are Copyright (C) 2009\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *      Kevin Dangoor (kdangoor@mozilla.com)\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\nvar deps = [\n    \"pilot/fixoldbrowsers\",\n    \"pilot/index\",\n    \"pilot/plugin_manager\",\n    \"pilot/environment\",\n    \"ace/editor\",\n    \"ace/edit_session\",\n    \"ace/virtual_renderer\",\n    \"ace/undomanager\",\n    \"ace/theme/textmate\"\n];\n\nrequire(deps, function() {\n    var catalog = require(\"pilot/plugin_manager\").catalog;\n    catalog.registerPlugins([ \"pilot/index\" ]);\n\n    var Dom = require(\"pilot/dom\");\n    var Event = require(\"pilot/event\");\n\n    var Editor = require(\"ace/editor\").Editor;\n    var EditSession = require(\"ace/edit_session\").EditSession;\n    var UndoManager = require(\"ace/undomanager\").UndoManager;\n    var Renderer = require(\"ace/virtual_renderer\").VirtualRenderer;\n\n    window.ace = {\n        edit: function(el) {\n            if (typeof(el) == \"string\") {\n                el = document.getElementById(el);\n            }\n\n            var doc = new EditSession(Dom.getInnerText(el));\n            doc.setUndoManager(new UndoManager());\n            el.innerHTML = '';\n\n            var editor = new Editor(new Renderer(el, \"ace/theme/textmate\"));\n            editor.setSession(doc);\n\n            var env = require(\"pilot/environment\").create();\n            catalog.startupPlugins({ env: env }).then(function() {\n                env.document = doc;\n                env.editor = editor;\n                editor.resize();\n                Event.addListener(window, \"resize\", function() {\n                    editor.resize();\n                });\n                el.env = env;\n            });\n            // Store env on editor such that it can be accessed later on from\n            // the returned object.\n            editor.env = env;\n            return editor;\n        }\n    };\n});\n"
  },
  {
    "path": "dirigible/shared/static/ace/ace.js",
    "content": "(function(){if(window.require)require.packaged=!0;else{var a=function(b,c,d){typeof b!==\"string\"?a.original?a.original.apply(window,arguments):(console.error(\"dropping module because define wasn't a string.\"),console.trace()):(arguments.length==2&&(d=c),define.modules||(define.modules={}),define.modules[b]=d)};window.define&&(a.original=window.define),window.define=a;var b=function(a,d){if(Object.prototype.toString.call(a)===\"[object Array]\"){var e=[];for(var f=0,g=a.length;f<g;++f){var h=c(a[f]);if(!h&&b.original)return b.original.apply(window,arguments);e.push(h)}d&&d.apply(null,e)}else{if(typeof a===\"string\"){var i=c(a);if(!i&&b.original)return b.original.apply(window,arguments);d&&d();return i}if(b.original)return b.original.apply(window,arguments)}};window.require&&(b.original=window.require),window.require=b,require.packaged=!0;var c=function(a){var b=define.modules[a];if(b==null){console.error(\"Missing module: \"+a);return null}if(typeof b===\"function\"){var c={};b(require,c,{id:a,uri:\"\"}),define.modules[a]=c;return c}return b}}})(),define(\"pilot/fixoldbrowsers\",function(a,b,c){var d=Object.prototype.hasOwnProperty;Array.isArray||(Array.isArray=function(a){return Object.prototype.toString.call(a)==\"[object Array]\"}),Array.prototype.forEach||(Array.prototype.forEach=function(a,b){var c=+this.length;for(var d=0;d<c;d++)d in this&&a.call(b,this[d],d,this)}),Array.prototype.map||(Array.prototype.map=function(a){var b=+this.length;if(typeof a!=\"function\")throw new TypeError;var c=Array(b),d=arguments[1];for(var e=0;e<b;e++)e in this&&(c[e]=a.call(d,this[e],e,this));return c}),Array.prototype.filter||(Array.prototype.filter=function(a){var b=[],c=arguments[1];for(var d=0;d<this.length;d++)a.call(c,this[d])&&b.push(this[d]);return b}),Array.prototype.every||(Array.prototype.every=function(a){var b=arguments[1];for(var c=0;c<this.length;c++)if(!a.call(b,this[c]))return!1;return!0}),Array.prototype.some||(Array.prototype.some=function(a){var b=arguments[1];for(var c=0;c<this.length;c++)if(a.call(b,this[c]))return!0;return!1}),Array.prototype.reduce||(Array.prototype.reduce=function(a){var b=+this.length;if(typeof a!=\"function\")throw new TypeError;if(b==0&&arguments.length==1)throw new TypeError;var c=0;if(arguments.length>=2)var d=arguments[1];else do{if(c in this){d=this[c++];break}if(++c>=b)throw new TypeError}while(!0);for(;c<b;c++)c in this&&(d=a.call(null,d,this[c],c,this));return d}),Array.prototype.reduceRight||(Array.prototype.reduceRight=function(a){var b=+this.length;if(typeof a!=\"function\")throw new TypeError;if(b==0&&arguments.length==1)throw new TypeError;var c=b-1;if(arguments.length>=2)var d=arguments[1];else do{if(c in this){d=this[c--];break}if(--c<0)throw new TypeError}while(!0);for(;c>=0;c--)c in this&&(d=a.call(null,d,this[c],c,this));return d}),Array.prototype.indexOf||(Array.prototype.indexOf=function(a){var b=this.length;if(!b)return-1;var c=arguments[1]||0;if(c>=b)return-1;c<0&&(c+=b);for(;c<b;c++){if(!d.call(this,c))continue;if(a===this[c])return c}return-1}),Array.prototype.lastIndexOf||(Array.prototype.lastIndexOf=function(a){var b=this.length;if(!b)return-1;var c=arguments[1]||b;c<0&&(c+=b),c=Math.min(c,b-1);for(;c>=0;c--){if(!d.call(this,c))continue;if(a===this[c])return c}return-1}),Object.getPrototypeOf||(Object.getPrototypeOf=function(a){return a.__proto__||a.constructor.prototype}),Object.getOwnPropertyDescriptor||(Object.getOwnPropertyDescriptor=function(a,b){if(typeof a!==\"object\"&&typeof a!==\"function\"||a===null)throw new TypeError(\"Object.getOwnPropertyDescriptor called on a non-object\");return d.call(a,b)?{value:a[b],enumerable:!0,configurable:!0,writeable:!0}:undefined}),Object.getOwnPropertyNames||(Object.getOwnPropertyNames=function(a){return Object.keys(a)}),Object.create||(Object.create=function(a,b){var c;if(a===null)c={\"__proto__\":null};else{if(typeof a!=\"object\")throw new TypeError(\"typeof prototype[\"+typeof a+\"] != 'object'\");var d=function(){};d.prototype=a,c=new d}typeof b!==\"undefined\"&&Object.defineProperties(c,b);return c}),Object.defineProperty||(Object.defineProperty=function(a,b,c){if(typeof c==\"object\"&&a.__defineGetter__){if(d.call(c,\"value\")){!a.__lookupGetter__(b)&&!a.__lookupSetter__(b)&&(a[b]=c.value);if(d.call(c,\"get\")||d.call(c,\"set\"))throw new TypeError(\"Object doesn't support this action\")}else typeof c.get==\"function\"&&a.__defineGetter__(b,c.get);typeof c.set==\"function\"&&a.__defineSetter__(b,c.set)}return a}),Object.defineProperties||(Object.defineProperties=function(a,b){for(var c in b)d.call(b,c)&&Object.defineProperty(a,c,b[c]);return a}),Object.seal||(Object.seal=function(a){return a}),Object.freeze||(Object.freeze=function(a){return a});try{Object.freeze(function(){})}catch(e){Object.freeze=function(a){return function(b){return typeof b==\"function\"?b:a(b)}}(Object.freeze)}Object.preventExtensions||(Object.preventExtensions=function(a){return a}),Object.isSealed||(Object.isSealed=function(a){return!1}),Object.isFrozen||(Object.isFrozen=function(a){return!1}),Object.isExtensible||(Object.isExtensible=function(a){return!0});if(!Object.keys){var f=!0,g=[\"toString\",\"toLocaleString\",\"valueOf\",\"hasOwnProperty\",\"isPrototypeOf\",\"propertyIsEnumerable\",\"constructor\"],h=g.length;for(var i in {toString:null})f=!1;Object.keys=function(a){if(typeof a!==\"object\"&&typeof a!==\"function\"||a===null)throw new TypeError(\"Object.keys called on a non-object\");var b=[];for(var c in a)d.call(a,c)&&b.push(c);if(f)for(var e=0,i=h;e<i;e++){var j=g[e];d.call(a,j)&&b.push(j)}return b}}Date.prototype.toISOString||(Date.prototype.toISOString=function(){return this.getUTCFullYear()+\"-\"+(this.getUTCMonth()+1)+\"-\"+this.getUTCDate()+\"T\"+this.getUTCHours()+\":\"+this.getUTCMinutes()+\":\"+this.getUTCSeconds()+\"Z\"}),Date.now||(Date.now=function(){return(new Date).getTime()}),Date.prototype.toJSON||(Date.prototype.toJSON=function(a){if(typeof this.toISOString!=\"function\")throw new TypeError;return this.toISOString()}),isNaN(Date.parse(\"T00:00\"))&&(Date=function(a){var b=function(c,d,e,f,g,h,i){var j=arguments.length;if(this instanceof a){var k=j===1&&String(c)===c?new a(b.parse(c)):j>=7?new a(c,d,e,f,g,h,i):j>=6?new a(c,d,e,f,g,h):j>=5?new a(c,d,e,f,g):j>=4?new a(c,d,e,f):j>=3?new a(c,d,e):j>=2?new a(c,d):j>=1?new a(c):new a;k.constructor=b;return k}return a.apply(this,arguments)},c=new RegExp(\"^(?:((?:[+-]\\\\d\\\\d)?\\\\d\\\\d\\\\d\\\\d)(?:-(\\\\d\\\\d)(?:-(\\\\d\\\\d))?)?)?(?:T(\\\\d\\\\d):(\\\\d\\\\d)(?::(\\\\d\\\\d)(?:\\\\.(\\\\d\\\\d\\\\d))?)?)?(?:Z|([+-])(\\\\d\\\\d):(\\\\d\\\\d))?$\");for(var d in a)b[d]=a[d];b.now=a.now,b.UTC=a.UTC,b.prototype=a.prototype,b.prototype.constructor=b,b.parse=function(b){var d=c.exec(b);if(d){d.shift();var e=d[0]===undefined;for(var f=0;f<10;f++){if(f===7)continue;d[f]=+(d[f]||(f<3?1:0)),f===1&&d[f]--}if(e)return((d[3]*60+d[4])*60+d[5])*1e3+d[6];var g=(d[8]*60+d[9])*60*1e3;d[6]===\"-\"&&(g=-g);return a.UTC.apply(this,d.slice(0,7))+g}return a.parse.apply(this,arguments)};return b}(Date));var j=Array.prototype.slice;Function.prototype.bind||(Function.prototype.bind=function(a){var b=this;if(typeof b.apply!=\"function\"||typeof b.call!=\"function\")return new TypeError;var c=j.call(arguments),d=function(){if(this instanceof d){var a=Object.create(b.prototype);b.apply(a,c.concat(j.call(arguments)));return a}return b.call.apply(b,c.concat(j.call(arguments)))};d.bound=b,d.boundTo=a,d.boundArgs=c,d.length=typeof b==\"function\"?Math.max(b.length-c.length,0):0;return d});if(!String.prototype.trim){var k=/^\\s\\s*/,l=/\\s\\s*$/;String.prototype.trim=function(){return String(this).replace(k,\"\").replace(l,\"\")}}b.globalsLoaded=!0}),define(\"pilot/index\",function(a,b,c){b.startup=function(b,c){a(\"pilot/fixoldbrowsers\"),a(\"pilot/types/basic\").startup(b,c),a(\"pilot/types/command\").startup(b,c),a(\"pilot/types/settings\").startup(b,c),a(\"pilot/commands/settings\").startup(b,c),a(\"pilot/commands/basic\").startup(b,c),a(\"pilot/settings/canon\").startup(b,c),a(\"pilot/canon\").startup(b,c)},b.shutdown=function(b,c){a(\"pilot/types/basic\").shutdown(b,c),a(\"pilot/types/command\").shutdown(b,c),a(\"pilot/types/settings\").shutdown(b,c),a(\"pilot/commands/settings\").shutdown(b,c),a(\"pilot/commands/basic\").shutdown(b,c),a(\"pilot/settings/canon\").shutdown(b,c),a(\"pilot/canon\").shutdown(b,c)}}),define(\"pilot/types/basic\",function(a,b,c){function m(a){if(a instanceof e)this.subtype=a;else{if(typeof a!==\"string\")throw new Error(\"Can' handle array subtype\");this.subtype=d.getType(a);if(this.subtype==null)throw new Error(\"Unknown array subtype: \"+a)}}function l(a){if(typeof a.defer!==\"function\")throw new Error(\"Instances of DeferredType need typeSpec.defer to be a function that returns a type\");Object.keys(a).forEach(function(b){this[b]=a[b]},this)}function j(a){if(!Array.isArray(a.data)&&typeof a.data!==\"function\")throw new Error(\"instances of SelectionType need typeSpec.data to be an array or function that returns an array:\"+JSON.stringify(a));Object.keys(a).forEach(function(b){this[b]=a[b]},this)}var d=a(\"pilot/types\"),e=d.Type,f=d.Conversion,g=d.Status,h=new e;h.stringify=function(a){return a},h.parse=function(a){if(typeof a!=\"string\")throw new Error(\"non-string passed to text.parse()\");return new f(a)},h.name=\"text\";var i=new e;i.stringify=function(a){if(!a)return null;return\"\"+a},i.parse=function(a){if(typeof a!=\"string\")throw new Error(\"non-string passed to number.parse()\");if(a.replace(/\\s/g,\"\").length===0)return new f(null,g.INCOMPLETE,\"\");var b=new f(parseInt(a,10));isNaN(b.value)&&(b.status=g.INVALID,b.message=\"Can't convert \\\"\"+a+'\" to a number.');return b},i.decrement=function(a){return a-1},i.increment=function(a){return a+1},i.name=\"number\",j.prototype=new e,j.prototype.stringify=function(a){return a},j.prototype.parse=function(a){if(typeof a!=\"string\")throw new Error(\"non-string passed to parse()\");if(!this.data)throw new Error(\"Missing data on selection type extension.\");var b=typeof this.data===\"function\"?this.data():this.data,c=!1,d,e=[];b.forEach(function(b){a==b?(d=this.fromString(b),c=!0):b.indexOf(a)===0&&e.push(this.fromString(b))},this);if(c)return new f(d);this.noMatch&&this.noMatch();if(e.length>0){var h=\"Possibilities\"+(a.length===0?\"\":\" for '\"+a+\"'\");return new f(null,g.INCOMPLETE,h,e)}var h=\"Can't use '\"+a+\"'.\";return new f(null,g.INVALID,h,e)},j.prototype.fromString=function(a){return a},j.prototype.decrement=function(a){var b=typeof this.data===\"function\"?this.data():this.data,c;if(a==null)c=b.length-1;else{var d=this.stringify(a),c=b.indexOf(d);c=c===0?b.length-1:c-1}return this.fromString(b[c])},j.prototype.increment=function(a){var b=typeof this.data===\"function\"?this.data():this.data,c;if(a==null)c=0;else{var d=this.stringify(a),c=b.indexOf(d);c=c===b.length-1?0:c+1}return this.fromString(b[c])},j.prototype.name=\"selection\",b.SelectionType=j;var k=new j({name:\"bool\",data:[\"true\",\"false\"],stringify:function(a){return\"\"+a},fromString:function(a){return a===\"true\"?!0:!1}});l.prototype=new e,l.prototype.stringify=function(a){return this.defer().stringify(a)},l.prototype.parse=function(a){return this.defer().parse(a)},l.prototype.decrement=function(a){var b=this.defer();return b.decrement?b.decrement(a):undefined},l.prototype.increment=function(a){var b=this.defer();return b.increment?b.increment(a):undefined},l.prototype.name=\"deferred\",b.DeferredType=l,m.prototype=new e,m.prototype.stringify=function(a){return a.join(\" \")},m.prototype.parse=function(a){return this.defer().parse(a)},m.prototype.name=\"array\";var n=!1;b.startup=function(){n||(n=!0,d.registerType(h),d.registerType(i),d.registerType(k),d.registerType(j),d.registerType(l),d.registerType(m))},b.shutdown=function(){n=!1,d.unregisterType(h),d.unregisterType(i),d.unregisterType(k),d.unregisterType(j),d.unregisterType(l),d.unregisterType(m)}}),define(\"pilot/types\",function(a,b,c){function i(a,b){if(a.substr(-2)===\"[]\"){var c=a.slice(0,-2);return new g.array(c)}var d=g[a];typeof d===\"function\"&&(d=new d(b));return d}function f(){}function e(a,b,c,e){this.value=a,this.status=b||d.VALID,this.message=c,this.predictions=e||[]}var d={VALID:{toString:function(){return\"VALID\"},valueOf:function(){return 0}},INCOMPLETE:{toString:function(){return\"INCOMPLETE\"},valueOf:function(){return 1}},INVALID:{toString:function(){return\"INVALID\"},valueOf:function(){return 2}},combine:function(a){var b=d.VALID;for(var c=0;c<a.length;c++)a[c].valueOf()>b.valueOf()&&(b=a[c]);return b}};b.Status=d,b.Conversion=e,f.prototype={stringify:function(a){throw new Error(\"not implemented\")},parse:function(a){throw new Error(\"not implemented\")},name:undefined,increment:function(a){return undefined},decrement:function(a){return undefined},getDefault:function(){return this.parse(\"\")}},b.Type=f;var g={};b.registerType=function(a){if(typeof a===\"object\"){if(!(a instanceof f))throw new Error(\"Can't registerType using: \"+a);if(!a.name)throw new Error(\"All registered types must have a name\");g[a.name]=a}else{if(typeof a!==\"function\")throw new Error(\"Unknown type: \"+a);if(!a.prototype.name)throw new Error(\"All registered types must have a name\");g[a.prototype.name]=a}},b.registerTypes=function h(a){Object.keys(a).forEach(function(c){var d=a[c];d.name=c,b.registerType(d)})},b.deregisterType=function(a){delete g[a.name]},b.getType=function(a){if(typeof a===\"string\")return i(a);if(typeof a===\"object\"){if(!a.name)throw new Error(\"Missing 'name' member to typeSpec\");return i(a.name,a)}throw new Error(\"Can't extract type from \"+a)}}),define(\"pilot/types/command\",function(a,b,c){var d=a(\"pilot/canon\"),e=a(\"pilot/types/basic\").SelectionType,f=a(\"pilot/types\"),g=new e({name:\"command\",data:function(){return d.getCommandNames()},stringify:function(a){return a.name},fromString:function(a){return d.getCommand(a)}});b.startup=function(){f.registerType(g)},b.shutdown=function(){f.unregisterType(g)}}),define(\"pilot/canon\",function(a,b,c){function J(a){a=a||{},this.command=a.command,this.args=a.args,this.typed=a.typed,this._begunOutput=!1,this.start=new Date,this.end=null,this.completed=!1,this.error=!1}function G(a,b,c,e,f){function h(){a.exec(b,g.args,g),!g.isAsync&&!g.isDone&&g.done()}typeof a===\"string\"&&(a=q[a]);if(!a)return!1;var g=new J({sender:c,command:a,args:e||{},typed:f});if(g.getStatus()==l.INVALID){d.error(\"Canon.exec: Invalid parameter(s) passed to \"+a.name);return!1}if(g.getStatus()==l.INCOMPLETE){var i,j=b[c];if(!j||!j.getArgsProvider||!(i=j.getArgsProvider()))i=F;i(g,function(){g.getStatus()==l.VALID&&h()});return!0}h();return!0}function F(a,b){var c=a.args,d=a.command.params;for(var e=0;e<d.length;e++){var f=d[e];if(a.getParamStatus(f)!=l.VALID||f.defaultValue===null){var g=f.description;f.defaultValue===null&&(g+=\" (optional)\");var h=prompt(g,f.defaultValue||\"\");if(!h){b();return}c[f.name]=h}}b()}function E(){return z}function D(a){return q[a]}function C(a){var b=typeof a===\"string\"?a:a.name;delete q[b],n.arrayRemove(z,b)}function B(a,b){var c=b.type;b.type=m.getType(c);if(b.type==null)throw new Error(\"In \"+a+\"/\"+b.name+\": can't find type for: \"+JSON.stringify(c))}function A(a){if(!a.name)throw new Error(\"All registered commands must have a name\");a.params==null&&(a.params=[]);if(!Array.isArray(a.params))throw new Error(\"command.params must be an array in \"+a.name);a.params.forEach(function(b){if(!b.name)throw new Error(\"In \"+a.name+\": all params must have a name\");B(a.name,b)},this),q[a.name]=a,a.bindKey&&w(a),z.push(a.name),z.sort()}function y(a,b,c,d){var e=x(a,b,c,d);return e?G(e,a,b,{}):!1}function x(a,b,c,d){j.isNumber(d)&&(d=h.keyCodeToString(d));var e=(s[c]||{})[d]||[];for(var f=0;f<e.length;f++)if(e[f].sender(a,b,c,d))return e[f].command;var g=r[b];return g&&g[c]&&g[c][d]}function w(a){var b=a.bindKey,c=b[v],d=r,e=s;if(!b.sender)throw new Error(\"All key bindings must have a sender\");if(!b.mac&&b.mac!==null)throw new Error(\"All key bindings must have a mac key binding\");if(!b.win&&b.win!==null)throw new Error(\"All key bindings must have a windows key binding\");if(!!b[v])if(typeof b.sender==\"string\"){var f=t(b.sender,\"\\\\|\",null,!0);f.forEach(function(b){d[b]||(d[b]={}),c.split(\"|\").forEach(function(c){u(c,a,d[b])})})}else{if(!j.isFunction(b.sender))throw new Error(\"Key binding must have a sender that is a string or function\");var g={command:a,sender:b.sender};keyData=u(c),e[keyData.hashId]||(e[keyData.hashId]={}),e[keyData.hashId][keyData.key]?e[keyData.hashId][keyData.key].push(g):e[keyData.hashId][keyData.key]=[g]}}function u(a,b,c){var d,e=0,f=t(a,\"\\\\-\",null,!0),g=0,i=f.length;for(;g<i;++g)h.KEY_MODS[f[g]]?e=e|h.KEY_MODS[f[g]]:d=f[g]||\"-\";if(c==null)return{key:d,hashId:e};(c[e]||(c[e]={}))[d]=b}function t(a,b,c,d){return(d&&a.toLowerCase()||a).replace(/(?:^\\s+|\\n|\\s+$)/g,\"\").split(new RegExp(\"[\\\\s ]*\"+b+\"[\\\\s ]*\",\"g\"),c||999)}var d=a(\"pilot/console\"),e=a(\"pilot/stacktrace\").Trace,f=a(\"pilot/oop\"),g=a(\"pilot/useragent\"),h=a(\"pilot/keys\"),i=a(\"pilot/event_emitter\").EventEmitter,j=a(\"pilot/typecheck\"),k=a(\"pilot/catalog\"),l=a(\"pilot/types\").Status,m=a(\"pilot/types\"),n=a(\"pilot/lang\"),o={name:\"command\",description:\"A command is a bit of functionality with optional typed arguments which can do something small like moving the cursor around the screen, or large like cloning a project from VCS.\",indexOn:\"name\"};b.startup=function(a,b){k.addExtensionSpec(o)},b.shutdown=function(a,b){k.removeExtensionSpec(o)};var p={name:\"thing\",description:\"thing is an example command\",params:[{name:\"param1\",description:\"an example parameter\",type:\"text\",defaultValue:null}],exec:function(a,b,c){thing()}},q={},r={},s={},v=g.isMac?\"mac\":\"win\",z=[];b.removeCommand=C,b.addCommand=A,b.getCommand=D,b.getCommandNames=E,b.findKeyCommand=x,b.exec=G,b.execKeyCommand=y,b.upgradeType=B,f.implement(b,i);var H=[],I=100;f.implement(J.prototype,i),J.prototype.getParamStatus=function(a){var b=this.args||{};if(a.name in b){if(b[a.name]==null)return a.defaultValue===null?l.VALID:l.INCOMPLETE;var c,d=b[a.name].toString();try{c=a.type.parse(d)}catch(e){return l.INVALID}if(c.status!=l.VALID)return c.status}else if(a.defaultValue===undefined)return l.INCOMPLETE;return l.VALID},J.prototype.getParamNameStatus=function(a){var b=this.command.params||[];for(var c=0;c<b.length;c++)if(b[c].name==a)return this.getParamStatus(b[c]);throw\"Parameter '\"+a+\"' not defined on command '\"+this.command.name+\"'\"},J.prototype.getStatus=function(){var a=this.args||{},b=this.command.params;if(!b||b.length==0)return l.VALID;var c=[];for(var d=0;d<b.length;d++)c.push(this.getParamStatus(b[d]));return l.combine(c)},J.prototype._beginOutput=function(){this._begunOutput=!0,this.outputs=[],H.push(this);while(H.length>I)H.shiftObject();b._dispatchEvent(\"output\",{requests:H,request:this})},J.prototype.doneWithError=function(a){this.error=!0,this.done(a)},J.prototype.async=function(){this.isAsync=!0,this._begunOutput||this._beginOutput()},J.prototype.output=function(a){this._begunOutput||this._beginOutput(),typeof a!==\"string\"&&!(a instanceof Node)&&(a=a.toString()),this.outputs.push(a),this.isDone=!0,this._dispatchEvent(\"output\",{});return this},J.prototype.done=function(a){this.completed=!0,this.end=new Date,this.duration=this.end.getTime()-this.start.getTime(),a&&this.output(a),this.isDone||(this.isDone=!0,this._dispatchEvent(\"output\",{}))},b.Request=J}),define(\"pilot/console\",function(a,b,c){var d=function(){},e=[\"assert\",\"count\",\"debug\",\"dir\",\"dirxml\",\"error\",\"group\",\"groupEnd\",\"info\",\"log\",\"profile\",\"profileEnd\",\"time\",\"timeEnd\",\"trace\",\"warn\"];typeof window===\"undefined\"?e.forEach(function(a){b[a]=function(){var b=Array.prototype.slice.call(arguments),c={op:\"log\",method:a,args:b};postMessage(JSON.stringify(c))}}):e.forEach(function(a){window.console&&window.console[a]?b[a]=Function.prototype.bind.call(window.console[a],window.console):b[a]=d})}),define(\"pilot/stacktrace\",function(a,b,c){function i(){}function g(a){for(var b=0;b<a.length;++b){var c=a[b];typeof c==\"object\"?a[b]=\"#object\":typeof c==\"function\"?a[b]=\"#function\":typeof c==\"string\"&&(a[b]='\"'+c+'\"')}return a.join(\",\")}var d=a(\"pilot/useragent\"),e=a(\"pilot/console\"),f=function(){return d.isGecko?\"firefox\":d.isOpera?\"opera\":\"other\"}(),h={chrome:function(a){var b=a.stack;if(!b){e.log(a);return[]}return b.replace(/^.*?\\n/,\"\").replace(/^.*?\\n/,\"\").replace(/^.*?\\n/,\"\").replace(/^[^\\(]+?[\\n$]/gm,\"\").replace(/^\\s+at\\s+/gm,\"\").replace(/^Object.<anonymous>\\s*\\(/gm,\"{anonymous}()@\").split(\"\\n\")},firefox:function(a){var b=a.stack;if(!b){e.log(a);return[]}b=b.replace(/(?:\\n@:0)?\\s+$/m,\"\"),b=b.replace(/^\\(/gm,\"{anonymous}(\");return b.split(\"\\n\")},opera:function(a){var b=a.message.split(\"\\n\"),c=\"{anonymous}\",d=/Line\\s+(\\d+).*?script\\s+(http\\S+)(?:.*?in\\s+function\\s+(\\S+))?/i,e,f,g;for(e=4,f=0,g=b.length;e<g;e+=2)d.test(b[e])&&(b[f++]=(RegExp.$3?RegExp.$3+\"()@\"+RegExp.$2+RegExp.$1:c+\"()@\"+RegExp.$2+\":\"+RegExp.$1)+\" -- \"+b[e+1].replace(/^\\s+/,\"\"));b.splice(f,b.length-f);return b},other:function(a){var b=\"{anonymous}\",c=/function\\s*([\\w\\-$]+)?\\s*\\(/i,d=[],e=0,f,h,i=10;while(a&&d.length<i){f=c.test(a.toString())?RegExp.$1||b:b,h=Array.prototype.slice.call(a.arguments),d[e++]=f+\"(\"+g(h)+\")\";if(a===a.caller&&window.opera)break;a=a.caller}return d}};i.prototype={sourceCache:{},ajax:function(a){var b=this.createXMLHTTPObject();if(!!b){b.open(\"GET\",a,!1),b.setRequestHeader(\"User-Agent\",\"XMLHTTP/1.0\"),b.send(\"\");return b.responseText}},createXMLHTTPObject:function(){var a,b=[function(){return new XMLHttpRequest},function(){return new ActiveXObject(\"Msxml2.XMLHTTP\")},function(){return new ActiveXObject(\"Msxml3.XMLHTTP\")},function(){return new ActiveXObject(\"Microsoft.XMLHTTP\")}];for(var c=0;c<b.length;c++)try{a=b[c](),this.createXMLHTTPObject=b[c];return a}catch(d){}},getSource:function(a){a in this.sourceCache||(this.sourceCache[a]=this.ajax(a).split(\"\\n\"));return this.sourceCache[a]},guessFunctions:function(a){for(var b=0;b<a.length;++b){var c=/{anonymous}\\(.*\\)@(\\w+:\\/\\/([-\\w\\.]+)+(:\\d+)?[^:]+):(\\d+):?(\\d+)?/,d=a[b],e=c.exec(d);if(e){var f=e[1],g=e[4];if(f&&g){var h=this.guessFunctionName(f,g);a[b]=d.replace(\"{anonymous}\",h)}}}return a},guessFunctionName:function(a,b){try{return this.guessFunctionNameFromLines(b,this.getSource(a))}catch(c){return\"getSource failed with url: \"+a+\", exception: \"+c.toString()}},guessFunctionNameFromLines:function(a,b){var c=/function ([^(]*)\\(([^)]*)\\)/,d=/['\"]?([0-9A-Za-z_]+)['\"]?\\s*[:=]\\s*(function|eval|new Function)/,e=\"\",f=10;for(var g=0;g<f;++g){e=b[a-g]+e;if(e!==undefined){var h=d.exec(e);if(h)return h[1];h=c.exec(e);if(h&&h[1])return h[1]}}return\"(?)\"}};var j=new i,k=[/http:\\/\\/localhost:4020\\/sproutcore.js:/];b.ignoreFramesMatching=function(a){k.push(a)},b.Trace=function l(a,b){this._ex=a,this._stack=h[f](a),b&&(this._stack=j.guessFunctions(this._stack))},b.Trace.prototype.log=function(a){a<=0&&(a=999999999);var b=0;for(var c=0;c<this._stack.length&&b<a;c++){var d=this._stack[c],f=!0;k.forEach(function(a){a.test(d)&&(f=!1)}),f&&(e.debug(d),b++)}}}),define(\"pilot/useragent\",function(a,b,c){var d=(navigator.platform.match(/mac|win|linux/i)||[\"other\"])[0].toLowerCase(),e=navigator.userAgent,f=navigator.appVersion;b.isWin=d==\"win\",b.isMac=d==\"mac\",b.isLinux=d==\"linux\",b.isIE=!+\"\u000b1\",b.isGecko=b.isMozilla=window.controllers&&window.navigator.product===\"Gecko\",b.isOldGecko=b.isGecko&&/rv\\:1/.test(navigator.userAgent),b.isOpera=window.opera&&Object.prototype.toString.call(window.opera)==\"[object Opera]\",b.isWebKit=parseFloat(e.split(\"WebKit/\")[1])||undefined,b.isAIR=e.indexOf(\"AdobeAIR\")>=0,b.isIPad=e.indexOf(\"iPad\")>=0,b.OS={LINUX:\"LINUX\",MAC:\"MAC\",WINDOWS:\"WINDOWS\"},b.getOS=function(){return b.isMac?b.OS.MAC:b.isLinux?b.OS.LINUX:b.OS.WINDOWS}}),define(\"pilot/oop\",function(a,b,c){b.inherits=function(){var a=function(){};return function(b,c){a.prototype=c.prototype,b.super_=c.prototype,b.prototype=new a,b.prototype.constructor=b}}(),b.mixin=function(a,b){for(var c in b)a[c]=b[c]},b.implement=function(a,c){b.mixin(a,c)}}),define(\"pilot/keys\",function(a,b,c){var d=a(\"pilot/oop\"),e=function(){var a={MODIFIER_KEYS:{16:\"Shift\",17:\"Ctrl\",18:\"Alt\",224:\"Meta\"},KEY_MODS:{ctrl:1,alt:2,option:2,shift:4,meta:8,command:8},FUNCTION_KEYS:{8:\"Backspace\",9:\"Tab\",13:\"Return\",19:\"Pause\",27:\"Esc\",32:\"Space\",33:\"PageUp\",34:\"PageDown\",35:\"End\",36:\"Home\",37:\"Left\",38:\"Up\",39:\"Right\",40:\"Down\",44:\"Print\",45:\"Insert\",46:\"Delete\",112:\"F1\",113:\"F2\",114:\"F3\",115:\"F4\",116:\"F5\",117:\"F6\",118:\"F7\",119:\"F8\",120:\"F9\",121:\"F10\",122:\"F11\",123:\"F12\",144:\"Numlock\",145:\"Scrolllock\"},PRINTABLE_KEYS:{32:\" \",48:\"0\",49:\"1\",50:\"2\",51:\"3\",52:\"4\",53:\"5\",54:\"6\",55:\"7\",56:\"8\",57:\"9\",59:\";\",61:\"=\",65:\"a\",66:\"b\",67:\"c\",68:\"d\",69:\"e\",70:\"f\",71:\"g\",72:\"h\",73:\"i\",74:\"j\",75:\"k\",76:\"l\",77:\"m\",78:\"n\",79:\"o\",80:\"p\",81:\"q\",82:\"r\",83:\"s\",84:\"t\",85:\"u\",86:\"v\",87:\"w\",88:\"x\",89:\"y\",90:\"z\",107:\"+\",109:\"-\",110:\".\",188:\",\",190:\".\",191:\"/\",192:\"`\",219:\"[\",220:\"\\\\\",221:\"]\",222:'\"'}};for(i in a.FUNCTION_KEYS){var b=a.FUNCTION_KEYS[i].toUpperCase();a[b]=parseInt(i,10)}d.mixin(a,a.MODIFIER_KEYS),d.mixin(a,a.PRINTABLE_KEYS),d.mixin(a,a.FUNCTION_KEYS);return a}();d.mixin(b,e),b.keyCodeToString=function(a){return(e[a]||String.fromCharCode(a)).toLowerCase()}}),define(\"pilot/event_emitter\",function(a,b,c){var d={};d._emit=d._dispatchEvent=function(a,b){this._eventRegistry=this._eventRegistry||{};var c=this._eventRegistry[a];if(!!c&&!!c.length){var b=b||{};b.type=a;for(var d=0;d<c.length;d++)c[d](b)}},d.on=d.addEventListener=function(a,b){this._eventRegistry=this._eventRegistry||{};var c=this._eventRegistry[a];if(!c)var c=this._eventRegistry[a]=[];c.indexOf(b)==-1&&c.push(b)},d.removeListener=d.removeEventListener=function(a,b){this._eventRegistry=this._eventRegistry||{};var c=this._eventRegistry[a];if(!!c){var d=c.indexOf(b);d!==-1&&c.splice(d,1)}},d.removeAllListeners=function(a){this._eventRegistry&&(this._eventRegistry[a]=[])},b.EventEmitter=d}),define(\"pilot/typecheck\",function(a,b,c){var d=Object.prototype.toString;b.isString=function(a){return a&&d.call(a)===\"[object String]\"},b.isBoolean=function(a){return a&&d.call(a)===\"[object Boolean]\"},b.isNumber=function(a){return a&&d.call(a)===\"[object Number]\"&&isFinite(a)},b.isObject=function(a){return a!==undefined&&(a===null||typeof a==\"object\"||Array.isArray(a)||b.isFunction(a))},b.isFunction=function(a){return a&&d.call(a)===\"[object Function]\"}}),define(\"pilot/catalog\",function(a,b,c){var d={};b.addExtensionSpec=function(a){d[a.name]=a},b.removeExtensionSpec=function(a){typeof a===\"string\"?delete d[a]:delete d[a.name]},b.getExtensionSpec=function(a){return d[a]},b.getExtensionSpecs=function(){return Object.keys(d)}}),define(\"pilot/lang\",function(a,b,c){b.stringReverse=function(a){return a.split(\"\").reverse().join(\"\")},b.stringRepeat=function(a,b){return Array(b+1).join(a)},b.copyObject=function(a){var b={};for(var c in a)b[c]=a[c];return b},b.arrayToMap=function(a){var b={};for(var c=0;c<a.length;c++)b[a[c]]=1;return b},b.arrayRemove=function(a,b){for(var c=0;c<=a.length;c++)b===a[c]&&a.splice(c,1)},b.escapeRegExp=function(a){return a.replace(/([.*+?^${}()|[\\]\\/\\\\])/g,\"\\\\$1\")},b.deferredCall=function(a){var b=null,c=function(){b=null,a()},d=function(a){b||(b=setTimeout(c,a||0));return d};d.schedule=d,d.call=function(){this.cancel(),a();return d},d.cancel=function(){clearTimeout(b),b=null;return d};return d}}),define(\"pilot/types/settings\",function(a,b,c){var d=a(\"pilot/types/basic\").SelectionType,e=a(\"pilot/types/basic\").DeferredType,f=a(\"pilot/types\"),g=a(\"pilot/settings\").settings,h,i=new d({name:\"setting\",data:function(){return k.settings.getSettingNames()},stringify:function(a){h=a;return a.name},fromString:function(a){h=g.getSetting(a);return h},noMatch:function(){h=null}}),j=new e({name:\"settingValue\",defer:function(){return h?h.type:f.getType(\"text\")},getDefault:function(){var a=this.parse(\"\");if(h){var b=h.get();if(a.predictions.length===0)a.predictions.push(b);else{var c=!1;while(!0){var d=a.predictions.indexOf(b);if(d===-1)break;a.predictions.splice(d,1),c=!0}c&&a.predictions.push(b)}}return a}}),k;b.startup=function(a,b){k=a.env,f.registerType(i),f.registerType(j)},b.shutdown=function(a,b){f.unregisterType(i),f.unregisterType(j)}}),define(\"pilot/settings\",function(a,b,c){function n(){}function k(a){this._deactivated={},this._settings={},this._settingNames=[],a&&this.setPersister(a)}function j(a,b){this._settings=b,Object.keys(a).forEach(function(b){this[b]=a[b]},this),this.type=f.getType(this.type);if(this.type==null)throw new Error(\"In \"+this.name+\": can't find type for: \"+JSON.stringify(a.type));if(!this.name)throw new Error(\"Setting.name == undefined. Ignoring.\",this);if(!this.defaultValue===undefined)throw new Error(\"Setting.defaultValue == undefined\",this);this.onChange&&this.on(\"change\",this.onChange.bind(this)),this.set(this.defaultValue)}var d=a(\"pilot/console\"),e=a(\"pilot/oop\"),f=a(\"pilot/types\"),g=a(\"pilot/event_emitter\").EventEmitter,h=a(\"pilot/catalog\"),i={name:\"setting\",description:\"A setting is something that the application offers as a way to customize how it works\",register:\"env.settings.addSetting\",indexOn:\"name\"};b.startup=function(a,b){h.addExtensionSpec(i)},b.shutdown=function(a,b){h.removeExtensionSpec(i)},j.prototype={get:function(){return this.value},set:function(a){this.value!==a&&(this.value=a,this._settings.persister&&this._settings.persister.persistValue(this._settings,this.name,a),this._dispatchEvent(\"change\",{setting:this,value:a}))},resetValue:function(){this.set(this.defaultValue)}},e.implement(j.prototype,g),k.prototype={addSetting:function(a){var b=new j(a,this);this._settings[b.name]=b,this._settingNames.push(b.name),this._settingNames.sort()},addSettings:function l(a){Object.keys(a).forEach(function(b){var c=a[b];\"name\"in c||(c.name=b),this.addSetting(c)},this)},removeSetting:function(a){var b=typeof a===\"string\"?a:a.name;a=this._settings[b],delete this._settings[b],util.arrayRemove(this._settingNames,b),settings.removeAllListeners(\"change\")},removeSettings:function m(a){Object.keys(a).forEach(function(b){var c=a[b];\"name\"in c||(c.name=b),this.removeSettings(c)},this)},getSettingNames:function(){return this._settingNames},getSetting:function(a){return this._settings[a]},setPersister:function(a){this._persister=a,a&&a.loadInitialValues(this)},resetAll:function(){this.getSettingNames().forEach(function(a){this.resetValue(a)},this)},_list:function(){var a=[];this.getSettingNames().forEach(function(b){a.push({key:b,value:this.getSetting(b).get()})},this);return a},_loadDefaultValues:function(){this._loadFromObject(this._getDefaultValues())},_loadFromObject:function(a){for(var b in a)if(a.hasOwnProperty(b)){var c=this._settings[b];if(c){var d=c.type.parse(a[b]);this.set(b,d)}else this.set(b,a[b])}},_saveToObject:function(){return this.getSettingNames().map(function(a){return this._settings[a].type.stringify(this.get(a))}.bind(this))},_getDefaultValues:function(){return this.getSettingNames().map(function(a){return this._settings[a].spec.defaultValue}.bind(this))}},b.settings=new k,n.prototype={loadInitialValues:function(a){a._loadDefaultValues();var b=cookie.get(\"settings\");a._loadFromObject(JSON.parse(b))},persistValue:function(a,b,c){try{var e=JSON.stringify(a._saveToObject());cookie.set(\"settings\",e)}catch(f){d.error(\"Unable to JSONify the settings! \"+f);return}}},b.CookiePersister=n}),define(\"pilot/commands/settings\",function(a,b,c){var d={name:\"set\",params:[{name:\"setting\",type:\"setting\",description:\"The name of the setting to display or alter\",defaultValue:null},{name:\"value\",type:\"settingValue\",description:\"The new value for the chosen setting\",defaultValue:null}],description:\"define and show settings\",exec:function(a,b,c){var d;if(!b.setting){var e=a.settings.getSettingNames();d=\"\",e.sort(function(a,b){return a.localeCompare(b)}),e.forEach(function(b){var c=a.settings.getSetting(b),e=\"https://wiki.mozilla.org/Labs/Skywriter/Settings#\"+c.name;d+='<a class=\"setting\" href=\"'+e+'\" title=\"View external documentation on setting: '+c.name+'\" target=\"_blank\">'+c.name+\"</a> = \"+c.value+\"<br/>\"})}else b.value===undefined?d=\"<strong>\"+setting.name+\"</strong> = \"+setting.get():(b.setting.set(b.value),d=\"Setting: <strong>\"+b.setting.name+\"</strong> = \"+b.setting.get());c.done(d)}},e={name:\"unset\",params:[{name:\"setting\",type:\"setting\",description:\"The name of the setting to return to defaults\"}],description:\"unset a setting entirely\",exec:function(a,b,c){var d=a.settings.get(b.setting);d?(d.reset(),c.done(\"Reset \"+d.name+\" to default: \"+a.settings.get(b.setting))):c.doneWithError(\"No setting with the name <strong>\"+b.setting+\"</strong>.\")}},f=a(\"pilot/canon\");b.startup=function(a,b){f.addCommand(d),f.addCommand(e)},b.shutdown=function(a,b){f.removeCommand(d),f.removeCommand(e)}}),define(\"pilot/commands/basic\",function(require,exports,module){var checks=require(\"pilot/typecheck\"),canon=require(\"pilot/canon\"),helpMessages={plainPrefix:'<h2>Welcome to Skywriter - Code in the Cloud</h2><ul><li><a href=\"http://labs.mozilla.com/projects/skywriter\" target=\"_blank\">Home Page</a></li><li><a href=\"https://wiki.mozilla.org/Labs/Skywriter\" target=\"_blank\">Wiki</a></li><li><a href=\"https://wiki.mozilla.org/Labs/Skywriter/UserGuide\" target=\"_blank\">User Guide</a></li><li><a href=\"https://wiki.mozilla.org/Labs/Skywriter/Tips\" target=\"_blank\">Tips and Tricks</a></li><li><a href=\"https://wiki.mozilla.org/Labs/Skywriter/FAQ\" target=\"_blank\">FAQ</a></li><li><a href=\"https://wiki.mozilla.org/Labs/Skywriter/DeveloperGuide\" target=\"_blank\">Developers Guide</a></li></ul>',plainSuffix:'For more information, see the <a href=\"https://wiki.mozilla.org/Labs/Skywriter\">Skywriter Wiki</a>.'},helpCommandSpec={name:\"help\",params:[{name:\"search\",type:\"text\",description:\"Search string to narrow the output.\",defaultValue:null}],description:\"Get help on the available commands.\",exec:function(a,b,c){var d=[],e=canon.getCommand(b.search);if(e&&e.exec)d.push(e.description?e.description:\"No description for \"+b.search);else{var f=!1;!b.search&&helpMessages.plainPrefix&&d.push(helpMessages.plainPrefix),e?(d.push(\"<h2>Sub-Commands of \"+e.name+\"</h2>\"),d.push(\"<p>\"+e.description+\"</p>\")):b.search?(b.search==\"hidden\"&&(b.search=\"\",f=!0),d.push(\"<h2>Commands starting with '\"+b.search+\"':</h2>\")):d.push(\"<h2>Available Commands:</h2>\");var g=canon.getCommandNames();g.sort(),d.push(\"<table>\");for(var h=0;h<g.length;h++){e=canon.getCommand(g[h]);if(!f&&e.hidden)continue;if(e.description===undefined)continue;if(b.search&&e.name.indexOf(b.search)!==0)continue;if(!b.search&&e.name.indexOf(\" \")!=-1)continue;if(e&&e.name==b.search)continue;d.push(\"<tr>\"),d.push('<th class=\"right\">'+e.name+\"</th>\"),d.push(\"<td>\"+e.description+\"</td>\"),d.push(\"</tr>\")}d.push(\"</table>\"),!b.search&&helpMessages.plainSuffix&&d.push(helpMessages.plainSuffix)}c.done(d.join(\"\"))}},evalCommandSpec={name:\"eval\",params:[{name:\"javascript\",type:\"text\",description:\"The JavaScript to evaluate\"}],description:\"evals given js code and show the result\",hidden:!0,exec:function(env,args,request){var result,javascript=args.javascript;try{result=eval(javascript)}catch(e){result=\"<b>Error: \"+e.message+\"</b>\"}var msg=\"\",type=\"\",x;if(checks.isFunction(result))msg=(result+\"\").replace(/\\n/g,\"<br>\").replace(/ /g,\"&#160\"),type=\"function\";else if(checks.isObject(result)){Array.isArray(result)?type=\"array\":type=\"object\";var items=[],value;for(x in result)result.hasOwnProperty(x)&&(checks.isFunction(result[x])?value=\"[function]\":checks.isObject(result[x])?value=\"[object]\":value=result[x],items.push({name:x,value:value}));items.sort(function(a,b){return a.name.toLowerCase()<b.name.toLowerCase()?-1:1});for(x=0;x<items.length;x++)msg+=\"<b>\"+items[x].name+\"</b>: \"+items[x].value+\"<br>\"}else msg=result,type=typeof result;request.done(\"Result for eval <b>'\"+javascript+\"'</b>\"+\" (type: \"+type+\"): <br><br>\"+msg)}},versionCommandSpec={name:\"version\",description:\"show the Skywriter version\",hidden:!0,exec:function(a,b,c){var d=\"Skywriter \"+skywriter.versionNumber+\" (\"+skywriter.versionCodename+\")\";c.done(d)}},skywriterCommandSpec={name:\"skywriter\",hidden:!0,exec:function(a,b,c){var d=Math.floor(Math.random()*messages.length);c.done(\"Skywriter \"+messages[d])}},messages=[\"really wants you to trick it out in some way.\",\"is your Web editor.\",\"would love to be like Emacs on the Web.\",\"is written on the Web platform, so you can tweak it.\"],canon=require(\"pilot/canon\");exports.startup=function(a,b){canon.addCommand(helpCommandSpec),canon.addCommand(evalCommandSpec),canon.addCommand(skywriterCommandSpec)},exports.shutdown=function(a,b){canon.removeCommand(helpCommandSpec),canon.removeCommand(evalCommandSpec),canon.removeCommand(skywriterCommandSpec)}}),define(\"pilot/settings/canon\",function(a,b,c){var d={name:\"historyLength\",description:\"How many typed commands do we recall for reference?\",type:\"number\",defaultValue:50};b.startup=function(a,b){a.env.settings.addSetting(d)},b.shutdown=function(a,b){a.env.settings.removeSetting(d)}}),define(\"pilot/plugin_manager\",function(a,b,c){var d=a(\"pilot/promise\").Promise;b.REASONS={APP_STARTUP:1,APP_SHUTDOWN:2,PLUGIN_ENABLE:3,PLUGIN_DISABLE:4,PLUGIN_INSTALL:5,PLUGIN_UNINSTALL:6,PLUGIN_UPGRADE:7,PLUGIN_DOWNGRADE:8},b.Plugin=function(a){this.name=a,this.status=this.INSTALLED},b.Plugin.prototype={NEW:0,INSTALLED:1,REGISTERED:2,STARTED:3,UNREGISTERED:4,SHUTDOWN:5,install:function(b,c){var e=new d;if(this.status>this.NEW){e.resolve(this);return e}a([this.name],function(a){a.install&&a.install(b,c),this.status=this.INSTALLED,e.resolve(this)}.bind(this));return e},register:function(b,c){var e=new d;if(this.status!=this.INSTALLED){e.resolve(this);return e}a([this.name],function(a){a.register&&a.register(b,c),this.status=this.REGISTERED,e.resolve(this)}.bind(this));return e},startup:function(c,e){e=e||b.REASONS.APP_STARTUP;var f=new d;if(this.status!=this.REGISTERED){f.resolve(this);return f}a([this.name],function(a){a.startup&&a.startup(c,e),this.status=this.STARTED,f.resolve(this)}.bind(this));return f},shutdown:function(b,c){this.status==this.STARTED&&(pluginModule=a(this.name),pluginModule.shutdown&&pluginModule.shutdown(b,c))}},b.PluginCatalog=function(){this.plugins={}},b.PluginCatalog.prototype={registerPlugins:function(a,c,e){var f=[];a.forEach(function(a){var d=this.plugins[a];d===undefined&&(d=new b.Plugin(a),this.plugins[a]=d,f.push(d.register(c,e)))}.bind(this));return d.group(f)},startupPlugins:function(a,b){var c=[];for(var e in this.plugins){var f=this.plugins[e];c.push(f.startup(a,b))}return d.group(c)}},b.catalog=new b.PluginCatalog}),define(\"pilot/promise\",function(a,b,c){var d=a(\"pilot/console\"),e=a(\"pilot/stacktrace\").Trace,f=-1,g=0,h=1,i=0,j=!1,k=[],l=[];Promise=function(){this._status=g,this._value=undefined,this._onSuccessHandlers=[],this._onErrorHandlers=[],this._id=i++,k[this._id]=this},Promise.prototype.isPromise=!0,Promise.prototype.isComplete=function(){return this._status!=g},Promise.prototype.isResolved=function(){return this._status==h},Promise.prototype.isRejected=function(){return this._status==f},Promise.prototype.then=function(a,b){typeof a===\"function\"&&(this._status===h?a.call(null,this._value):this._status===g&&this._onSuccessHandlers.push(a)),typeof b===\"function\"&&(this._status===f?b.call(null,this._value):this._status===g&&this._onErrorHandlers.push(b));return this},Promise.prototype.chainPromise=function(a){var b=new Promise;b._chainedFrom=this,this.then(function(c){try{b.resolve(a(c))}catch(d){b.reject(d)}},function(a){b.reject(a)});return b},Promise.prototype.resolve=function(a){return this._complete(this._onSuccessHandlers,h,a,\"resolve\")},Promise.prototype.reject=function(a){return this._complete(this._onErrorHandlers,f,a,\"reject\")},Promise.prototype._complete=function(a,b,c,f){if(this._status!=g){d.group(\"Promise already closed\"),d.error(\"Attempted \"+f+\"() with \",c),d.error(\"Previous status = \",this._status,\", previous value = \",this._value),d.trace(),this._completeTrace&&(d.error(\"Trace of previous completion:\"),this._completeTrace.log(5)),d.groupEnd();return this}j&&(this._completeTrace=new e(new Error)),this._status=b,this._value=c,a.forEach(function(a){a.call(null,this._value)},this),this._onSuccessHandlers.length=0,this._onErrorHandlers.length=0,delete k[this._id],l.push(this);while(l.length>20)l.shift();return this},Promise.group=function(a){a instanceof Array||(a=Array.prototype.slice.call(arguments));if(a.length===0)return(new Promise).resolve([]);var b=new Promise,c=[],d=0,e=function(e){return function(g){c[e]=g,d++,b._status!==f&&d===a.length&&b.resolve(c)}};a.forEach(function(a,c){var d=e(c),f=b.reject.bind(b);a.then(d,f)});return b},b.Promise=Promise,b._outstanding=k,b._recent=l}),define(\"pilot/environment\",function(a,b,c){function e(){return{settings:d}}var d=a(\"pilot/settings\").settings;b.create=e}),define(\"ace/editor\",function(a,b,c){a(\"pilot/fixoldbrowsers\");var d=a(\"pilot/oop\"),e=a(\"pilot/event\"),f=a(\"pilot/lang\"),g=a(\"pilot/useragent\"),h=a(\"ace/keyboard/textinput\").TextInput,i=a(\"ace/mouse_handler\").MouseHandler,j=a(\"ace/keyboard/keybinding\").KeyBinding,k=a(\"ace/edit_session\").EditSession,l=a(\"ace/search\").Search,m=a(\"ace/background_tokenizer\").BackgroundTokenizer,n=a(\"ace/range\").Range,o=a(\"pilot/event_emitter\").EventEmitter,p=function(a,b){var c=a.getContainerElement();this.container=c,this.renderer=a,this.textInput=new h(a.getTextAreaContainer(),this),this.keyBinding=new j(this),g.isIPad||(this.$mouseHandler=new i(this)),this.$blockScrolling=0,this.$search=(new l).set({wrap:!0}),this.setSession(b||new k(\"\"))};(function(){d.implement(this,o),this.$forwardEvents={gutterclick:1,gutterdblclick:1},this.$originalAddEventListener=this.addEventListener,this.$originalRemoveEventListener=this.removeEventListener,this.addEventListener=function(a,b){return this.$forwardEvents[a]?this.renderer.addEventListener(a,b):this.$originalAddEventListener(a,b)},this.removeEventListener=function(a,b){return this.$forwardEvents[a]?this.renderer.removeEventListener(a,b):this.$originalRemoveEventListener(a,b)},this.setKeyboardHandler=function(a){this.keyBinding.setKeyboardHandler(a)},this.getKeyboardHandler=function(){return this.keyBinding.getKeyboardHandler()},this.setSession=function(a){if(this.session!=a){if(this.session){var b=this.session;this.session.removeEventListener(\"change\",this.$onDocumentChange),this.session.removeEventListener(\"changeMode\",this.$onChangeMode),this.session.removeEventListener(\"changeTabSize\",this.$onChangeTabSize),this.session.removeEventListener(\"changeWrapLimit\",this.$onChangeWrapLimit),this.session.removeEventListener(\"changeWrapMode\",this.$onChangeWrapMode),this.session.removeEventListener(\"changeFrontMarker\",this.$onChangeFrontMarker),this.session.removeEventListener(\"changeBackMarker\",this.$onChangeBackMarker),this.session.removeEventListener(\"changeBreakpoint\",this.$onChangeBreakpoint),this.session.removeEventListener(\"changeAnnotation\",this.$onChangeAnnotation),this.session.removeEventListener(\"changeOverwrite\",this.$onCursorChange);var c=this.session.getSelection();c.removeEventListener(\"changeCursor\",this.$onCursorChange),c.removeEventListener(\"changeSelection\",this.$onSelectionChange),this.session.setScrollTopRow(this.renderer.getScrollTopRow())}this.session=a,this.$onDocumentChange=this.onDocumentChange.bind(this),a.addEventListener(\"change\",this.$onDocumentChange),this.renderer.setSession(a),this.$onChangeMode=this.onChangeMode.bind(this),a.addEventListener(\"changeMode\",this.$onChangeMode),this.$onChangeTabSize=this.renderer.updateText.bind(this.renderer),a.addEventListener(\"changeTabSize\",this.$onChangeTabSize),this.$onChangeWrapLimit=this.onChangeWrapLimit.bind(this),a.addEventListener(\"changeWrapLimit\",this.$onChangeWrapLimit),this.$onChangeWrapMode=this.onChangeWrapMode.bind(this),a.addEventListener(\"changeWrapMode\",this.$onChangeWrapMode),this.$onChangeFrontMarker=this.onChangeFrontMarker.bind(this),this.session.addEventListener(\"changeFrontMarker\",this.$onChangeFrontMarker),this.$onChangeBackMarker=this.onChangeBackMarker.bind(this),this.session.addEventListener(\"changeBackMarker\",this.$onChangeBackMarker),this.$onChangeBreakpoint=this.onChangeBreakpoint.bind(this),this.session.addEventListener(\"changeBreakpoint\",this.$onChangeBreakpoint),this.$onChangeAnnotation=this.onChangeAnnotation.bind(this),this.session.addEventListener(\"changeAnnotation\",this.$onChangeAnnotation),this.$onCursorChange=this.onCursorChange.bind(this),this.session.addEventListener(\"changeOverwrite\",this.$onCursorChange),this.selection=a.getSelection(),this.selection.addEventListener(\"changeCursor\",this.$onCursorChange),this.$onSelectionChange=this.onSelectionChange.bind(this),this.selection.addEventListener(\"changeSelection\",this.$onSelectionChange),this.onChangeMode(),this.bgTokenizer.setDocument(a.getDocument()),this.bgTokenizer.start(0),this.onCursorChange(),this.onSelectionChange(),this.onChangeFrontMarker(),this.onChangeBackMarker(),this.onChangeBreakpoint(),this.onChangeAnnotation(),this.renderer.scrollToRow(a.getScrollTopRow()),this.renderer.updateFull(),this._dispatchEvent(\"changeSession\",{session:a,oldSession:b})}},this.getSession=function(){return this.session},this.getSelection=function(){return this.selection},this.resize=function(){this.renderer.onResize()},this.setTheme=function(a){this.renderer.setTheme(a)},this.setStyle=function(a){this.renderer.setStyle(a)},this.unsetStyle=function(a){this.renderer.unsetStyle(a)},this.$highlightBrackets=function(){this.session.$bracketHighlight&&(this.session.removeMarker(this.session.$bracketHighlight),this.session.$bracketHighlight=null);if(!this.$highlightPending){var a=this;this.$highlightPending=!0,setTimeout(function(){a.$highlightPending=!1;var b=a.session.findMatchingBracket(a.getCursorPosition());if(b){var c=new n(b.row,b.column,b.row,b.column+1);a.session.$bracketHighlight=a.session.addMarker(c,\"ace_bracket\")}},10)}},this.focus=function(){var a=this;g.isIE||setTimeout(function(){a.textInput.focus()}),this.textInput.focus()},this.blur=function(){this.textInput.blur()},this.onFocus=function(){this.renderer.showCursor(),this.renderer.visualizeFocus(),this._dispatchEvent(\"focus\")},this.onBlur=function(){this.renderer.hideCursor(),this.renderer.visualizeBlur(),this._dispatchEvent(\"blur\")},this.onDocumentChange=function(a){var b=a.data,c=b.range;this.bgTokenizer.start(c.start.row);if(c.start.row==c.end.row&&b.action!=\"insertLines\"&&b.action!=\"removeLines\")var d=c.end.row;else d=Infinity;this.renderer.updateLines(c.start.row,d),this.renderer.updateCursor()},this.onTokenizerUpdate=function(a){var b=a.data;this.renderer.updateLines(b.first,b.last)},this.onCursorChange=function(a){this.renderer.updateCursor(),this.$blockScrolling||this.renderer.scrollCursorIntoView(),this.renderer.moveTextAreaToCursor(this.textInput.getElement()),this.$highlightBrackets(),this.$updateHighlightActiveLine()},this.$updateHighlightActiveLine=function(){var a=this.getSession();a.$highlightLineMarker&&a.removeMarker(a.$highlightLineMarker),a.$highlightLineMarker=null;if(this.getHighlightActiveLine()&&(this.getSelectionStyle()!=\"line\"||!this.selection.isMultiLine())){var b=this.getCursorPosition(),c=new n(b.row,0,b.row+1,0);a.$highlightLineMarker=a.addMarker(c,\"ace_active_line\",\"line\")}},this.onSelectionChange=function(a){var b=this.getSession();b.$selectionMarker&&b.removeMarker(b.$selectionMarker),b.$selectionMarker=null;if(!this.selection.isEmpty()){var c=this.selection.getRange(),d=this.getSelectionStyle();b.$selectionMarker=b.addMarker(c,\"ace_selection\",d)}this.onCursorChange(a),this.$highlightSelectedWord&&this.mode.highlightSelection(this)},this.onChangeFrontMarker=function(){this.renderer.updateFrontMarkers()},this.onChangeBackMarker=function(){this.renderer.updateBackMarkers()},this.onChangeBreakpoint=function(){this.renderer.setBreakpoints(this.session.getBreakpoints())},this.onChangeAnnotation=function(){this.renderer.setAnnotations(this.session.getAnnotations())},this.onChangeMode=function(){var a=this.session.getMode();if(this.mode!=a){this.mode=a;var b=a.getTokenizer();if(!this.bgTokenizer){var c=this.onTokenizerUpdate.bind(this);this.bgTokenizer=new m(b,this),this.bgTokenizer.addEventListener(\"update\",c)}else this.bgTokenizer.setTokenizer(b);this.renderer.setTokenizer(this.bgTokenizer)}},this.onChangeWrapLimit=function(){this.renderer.updateFull()},this.onChangeWrapMode=function(){this.renderer.onResize(!0)},this.getCopyText=function(){return this.selection.isEmpty()?\"\":this.session.getTextRange(this.getSelectionRange())},this.onCut=function(){this.$readOnly||this.selection.isEmpty()||(this.session.remove(this.getSelectionRange()),this.clearSelection())},this.insert=function(a){if(!this.$readOnly){var b=this.getCursorPosition();a=a.replace(\"\\t\",this.session.getTabString());if(!this.selection.isEmpty()){var b=this.session.remove(this.getSelectionRange());this.clearSelection()}else if(this.session.getOverwrite()){var c=new n.fromPoints(b,b);c.end.column+=a.length,this.session.remove(c)}this.clearSelection();var d=this.bgTokenizer.getState(b.row),e=this.mode.checkOutdent(d,this.session.getLine(b.row),a),f=this.session.getLine(b.row),g=this.mode.getNextLineIndent(d,f.slice(0,b.column),this.session.getTabString()),h=this.session.insert(b,a),d=this.bgTokenizer.getState(b.row);if(this.session.getDocument().isNewLine(a)){this.moveCursorTo(b.row+1,0);var i=this.session.getTabSize(),j=Number.MAX_VALUE;for(var k=b.row+1;k<=h.row;++k){var l=0;f=this.session.getLine(k);for(var m=0;m<f.length;++m)if(f.charAt(m)==\"\\t\")l+=i;else if(f.charAt(m)==\" \")l+=1;else break;/[^\\s]/.test(f)&&(j=Math.min(l,j))}for(var k=b.row+1;k<=h.row;++k){var o=j;f=this.session.getLine(k);for(var m=0;m<f.length&&o>0;++m)f.charAt(m)==\"\\t\"?o-=i:f.charAt(m)==\" \"&&(o-=1);this.session.remove(new n(k,0,k,m))}this.session.indentRows(b.row+1,h.row,g)}else e&&this.mode.autoOutdent(d,this.session,b.row)}},this.onTextInput=function(a){this.keyBinding.onTextInput(a)},this.onCommandKey=function(a,b,c){this.keyBinding.onCommandKey(a,b,c)},this.setOverwrite=function(a){this.session.setOverwrite()},this.getOverwrite=function(){return this.session.getOverwrite()},this.toggleOverwrite=function(){this.session.toggleOverwrite()},this.setScrollSpeed=function(a){this.$mouseHandler.setScrollSpeed(a)},this.getScrollSpeed=function(){return this.$mouseHandler.getScrollSpeed()},this.$selectionStyle=\"line\",this.setSelectionStyle=function(a){this.$selectionStyle!=a&&(this.$selectionStyle=a,this.onSelectionChange(),this._dispatchEvent(\"changeSelectionStyle\",{data:a}))},this.getSelectionStyle=function(){return this.$selectionStyle},this.$highlightActiveLine=!0,this.setHighlightActiveLine=function(a){this.$highlightActiveLine!=a&&(this.$highlightActiveLine=a,this.$updateHighlightActiveLine())},this.getHighlightActiveLine=function(){return this.$highlightActiveLine},this.$highlightSelectedWord=!0,this.setHighlightSelectedWord=function(a){this.$highlightSelectedWord!=a&&(this.$highlightSelectedWord=a,a?this.mode.highlightSelection(this):this.mode.clearSelectionHighlight(this))},this.getHighlightSelectedWord=function(){return this.$highlightSelectedWord},this.setShowInvisibles=function(a){this.getShowInvisibles()!=a&&this.renderer.setShowInvisibles(a)},this.getShowInvisibles=function(){return this.renderer.getShowInvisibles()},this.setShowPrintMargin=function(a){this.renderer.setShowPrintMargin(a)},this.getShowPrintMargin=function(){return this.renderer.getShowPrintMargin()},this.setPrintMarginColumn=function(a){this.renderer.setPrintMarginColumn(a)},this.getPrintMarginColumn=function(){return this.renderer.getPrintMarginColumn()},this.$readOnly=!1,this.setReadOnly=function(a){this.$readOnly=a},this.getReadOnly=function(){return this.$readOnly},this.removeRight=function(){this.$readOnly||(this.selection.isEmpty()&&this.selection.selectRight(),this.session.remove(this.getSelectionRange()),this.clearSelection())},this.removeLeft=function(){this.$readOnly||(this.selection.isEmpty()&&this.selection.selectLeft(),this.session.remove(this.getSelectionRange()),this.clearSelection())},this.removeWordRight=function(){this.$readOnly||(this.selection.isEmpty()&&this.selection.selectWordRight(),this.session.remove(this.getSelectionRange()),this.clearSelection())},this.removeWordLeft=function(){this.$readOnly||(this.selection.isEmpty()&&this.selection.selectWordLeft(),this.session.remove(this.getSelectionRange()),this.clearSelection())},this.removeToLineStart=function(){this.$readOnly||(this.selection.isEmpty()&&this.selection.selectLineStart(),this.session.remove(this.getSelectionRange()),this.clearSelection())},this.removeToLineEnd=function(){this.$readOnly||(this.selection.isEmpty()&&this.selection.selectLineEnd(),this.session.remove(this.getSelectionRange()),this.clearSelection())},this.splitLine=function(){if(!this.$readOnly){this.selection.isEmpty()||(this.session.remove(this.getSelectionRange()),this.clearSelection());var a=this.getCursorPosition();this.insert(\"\\n\"),this.moveCursorToPosition(a)}},this.transposeLetters=function(){if(!this.$readOnly){if(!this.selection.isEmpty())return;var a=this.getCursorPosition(),b=a.column;if(b==0)return;var c=this.session.getLine(a.row);if(b<c.length)var d=c.charAt(b)+c.charAt(b-1),e=new n(a.row,b-1,a.row,b+1);else var d=c.charAt(b-1)+c.charAt(b-2),e=new n(a.row,b-2,a.row,b);this.session.replace(e,d)}},this.indent=function(){if(!this.$readOnly){var a=this.session,b=this.getSelectionRange();if(!(b.start.row<b.end.row||b.start.column<b.end.column)){var d;if(this.session.getUseSoftTabs()){var e=a.getTabSize(),g=this.getCursorPosition(),h=a.documentToScreenColumn(g.row,g.column),i=e-h%e;d=f.stringRepeat(\" \",i)}else d=\"\\t\";return this.onTextInput(d)}var c=this.$getSelectedRows();a.indentRows(c.first,c.last,\"\\t\")}},this.blockOutdent=function(){if(!this.$readOnly){var a=this.session.getSelection();this.session.outdentRows(a.getRange())}},this.toggleCommentLines=function(){if(!this.$readOnly){var a=this.bgTokenizer.getState(this.getCursorPosition().row),b=this.$getSelectedRows();this.mode.toggleCommentLines(a,this.session,b.first,b.last)}},this.removeLines=function(){if(!this.$readOnly){var a=this.$getSelectedRows();this.session.remove(new n(a.first,0,a.last+1,0)),this.clearSelection()}},this.moveLinesDown=function(){this.$readOnly||this.$moveLines(function(a,b){return this.session.moveLinesDown(a,b)})},this.moveLinesUp=function(){this.$readOnly||this.$moveLines(function(a,b){return this.session.moveLinesUp(a,b)})},this.moveText=function(a,b){if(this.$readOnly)return null;return this.session.moveText(a,b)},this.copyLinesUp=function(){this.$readOnly||this.$moveLines(function(a,b){this.session.duplicateLines(a,b);return 0})},this.copyLinesDown=function(){this.$readOnly||this.$moveLines(function(a,b){return this.session.duplicateLines(a,b)})},this.$moveLines=function(a){var b=this.$getSelectedRows(),c=a.call(this,b.first,b.last),d=this.selection;d.setSelectionAnchor(b.last+c+1,0),d.$moveSelection(function(){d.moveCursorTo(b.first+c,0)})},this.$getSelectedRows=function(){var a=this.getSelectionRange().collapseRows();return{first:a.start.row,last:a.end.row}},this.onCompositionStart=function(a){this.renderer.showComposition(this.getCursorPosition())},this.onCompositionUpdate=function(a){this.renderer.setCompositionText(a)},this.onCompositionEnd=function(){this.renderer.hideComposition()},this.getFirstVisibleRow=function(){return this.renderer.getFirstVisibleRow()},this.getLastVisibleRow=function(){return this.renderer.getLastVisibleRow()},this.isRowVisible=function(a){return a>=this.getFirstVisibleRow()&&a<=this.getLastVisibleRow()},this.$getVisibleRowCount=function(){return this.renderer.getScrollBottomRow()-this.renderer.getScrollTopRow()+1},this.$getPageDownRow=function(){return this.renderer.getScrollBottomRow()},this.$getPageUpRow=function(){var a=this.renderer.getScrollTopRow(),b=this.renderer.getScrollBottomRow();return a-(b-a)},this.selectPageDown=function(){var a=this.$getPageDownRow()+Math.floor(this.$getVisibleRowCount()/2);this.scrollPageDown();var b=this.getSelection(),c=this.session.documentToScreenPosition(b.getSelectionLead()),d=this.session.screenToDocumentPosition(a,c.column);b.selectTo(d.row,d.column)},this.selectPageUp=function(){var a=this.renderer.getScrollTopRow()-this.renderer.getScrollBottomRow(),b=this.$getPageUpRow()+Math.round(a/2);this.scrollPageUp();var c=this.getSelection(),d=this.session.documentToScreenPosition(c.getSelectionLead()),e=this.session.screenToDocumentPosition(b,d.column);c.selectTo(e.row,e.column)},this.gotoPageDown=function(){var a=this.$getPageDownRow(),b=this.getCursorPositionScreen().column;this.scrollToRow(a),this.getSelection().moveCursorToScreen(a,b)},this.gotoPageUp=function(){var a=this.$getPageUpRow(),b=this.getCursorPositionScreen().column;this.scrollToRow(a),this.getSelection().moveCursorToScreen(a,b)},this.scrollPageDown=function(){this.scrollToRow(this.$getPageDownRow())},this.scrollPageUp=function(){this.renderer.scrollToRow(this.$getPageUpRow())},this.scrollToRow=function(a){this.renderer.scrollToRow(a)},this.scrollToLine=function(a,b){this.renderer.scrollToLine(a,b)},this.centerSelection=function(){var a=this.getSelectionRange(),b=Math.floor(a.start.row+(a.end.row-a.start.row)/2);this.renderer.scrollToLine(b,!0)},this.getCursorPosition=function(){return this.selection.getCursor()},this.getCursorPositionScreen=function(){return this.session.documentToScreenPosition(this.getCursorPosition())},this.getSelectionRange=function(){return this.selection.getRange()},this.selectAll=function(){this.$blockScrolling+=1,this.selection.selectAll(),this.$blockScrolling-=1},this.clearSelection=function(){this.selection.clearSelection()},this.moveCursorTo=function(a,b){this.selection.moveCursorTo(a,b)},this.moveCursorToPosition=function(a){this.selection.moveCursorToPosition(a)},this.gotoLine=function(a,b){this.selection.clearSelection(),this.$blockScrolling+=1,this.moveCursorTo(a-1,b||0),this.$blockScrolling-=1,this.isRowVisible(this.getCursorPosition().row)||this.scrollToLine(a,!0)},this.navigateTo=function(a,b){this.clearSelection(),this.moveCursorTo(a,b)},this.navigateUp=function(a){this.selection.clearSelection(),a=a||1,this.selection.moveCursorBy(-a,0)},this.navigateDown=function(a){this.selection.clearSelection(),a=a||1,this.selection.moveCursorBy(a,0)},this.navigateLeft=function(a){if(!this.selection.isEmpty()){var b=this.getSelectionRange().start;this.moveCursorToPosition(b)}else{a=a||1;while(a--)this.selection.moveCursorLeft()}this.clearSelection()},this.navigateRight=function(a){if(!this.selection.isEmpty()){var b=this.getSelectionRange().end;this.moveCursorToPosition(b)}else{a=a||1;while(a--)this.selection.moveCursorRight()}this.clearSelection()},this.navigateLineStart=function(){this.selection.moveCursorLineStart(),this.clearSelection()},this.navigateLineEnd=function(){this.selection.moveCursorLineEnd(),this.clearSelection()},this.navigateFileEnd=function(){this.selection.moveCursorFileEnd(),this.clearSelection()},this.navigateFileStart=function(){this.selection.moveCursorFileStart(),this.clearSelection()},this.navigateWordRight=function(){this.selection.moveCursorWordRight(),this.clearSelection()},this.navigateWordLeft=function(){this.selection.moveCursorWordLeft(),this.clearSelection()},this.replace=function(a,b){b&&this.$search.set(b);var c=this.$search.find(this.session);this.$tryReplace(c,a),c!==null&&this.selection.setSelectionRange(c)},this.replaceAll=function(a,b){b&&this.$search.set(b);var c=this.$search.findAll(this.session);if(!!c.length){var d=this.getSelectionRange();this.clearSelection(),this.selection.moveCursorTo(0,0),this.$blockScrolling+=1;for(var e=c.length-1;e>=0;--e)this.$tryReplace(c[e],a);this.selection.setSelectionRange(d),this.$blockScrolling-=1}},this.$tryReplace=function(a,b){var c=this.session.getTextRange(a),b=this.$search.replace(c,b);if(b!==null){a.end=this.session.replace(a,b);return a}return null},this.getLastSearchOptions=function(){return this.$search.getOptions()},this.find=function(a,b){this.clearSelection(),b=b||{},b.needle=a,this.$search.set(b),this.$find()},this.findNext=function(a){a=a||{},typeof a.backwards==\"undefined\"&&(a.backwards=!1),this.$search.set(a),this.$find()},this.findPrevious=function(a){a=a||{},typeof a.backwards==\"undefined\"&&(a.backwards=!0),this.$search.set(a),this.$find()},this.$find=function(a){this.selection.isEmpty()||this.$search.set({needle:this.session.getTextRange(this.getSelectionRange())}),typeof a!=\"undefined\"&&this.$search.set({backwards:a});var b=this.$search.find(this.session);b&&(this.gotoLine(b.end.row+1,b.end.column),this.selection.setSelectionRange(b))},this.undo=function(){this.session.getUndoManager().undo()},this.redo=function(){this.session.getUndoManager().redo()}}).call(p.prototype),b.Editor=p}),define(\"pilot/event\",function(a,b,c){function g(a,b,c){var f=0;e.isOpera&&e.isMac?f=0|(b.metaKey?1:0)|(b.altKey?2:0)|(b.shiftKey?4:0)|(b.ctrlKey?8:0):f=0|(b.ctrlKey?1:0)|(b.altKey?2:0)|(b.shiftKey?4:0)|(b.metaKey?8:0);if(c in d.MODIFIER_KEYS){switch(d.MODIFIER_KEYS[c]){case\"Alt\":f=2;break;case\"Shift\":f=4;break;case\"Ctrl\":f=1;break;default:f=8}c=0}f&8&&(c==91||c==93)&&(c=0);if(f==0&&!(c in d.FUNCTION_KEYS))return!1;return a(b,f,c)}var d=a(\"pilot/keys\"),e=a(\"pilot/useragent\"),f=a(\"pilot/dom\");b.addListener=function(a,b,c){if(a.addEventListener)return a.addEventListener(b,c,!1);if(a.attachEvent){var d=function(){c(window.event)};c._wrapper=d,a.attachEvent(\"on\"+b,d)}},b.removeListener=function(a,b,c){if(a.removeEventListener)return a.removeEventListener(b,c,!1);a.detachEvent&&a.detachEvent(\"on\"+b,c._wrapper||c)},b.stopEvent=function(a){b.stopPropagation(a),b.preventDefault(a);return!1},b.stopPropagation=function(a){a.stopPropagation?a.stopPropagation():a.cancelBubble=!0},b.preventDefault=function(a){a.preventDefault?a.preventDefault():a.returnValue=!1},b.getDocumentX=function(a){return a.clientX?a.clientX+f.getPageScrollLeft():a.pageX},b.getDocumentY=function(a){return a.clientY?a.clientY+f.getPageScrollTop():a.pageY},b.getButton=function(a){if(a.type==\"dblclick\")return 0;if(a.type==\"contextmenu\")return 2;return a.preventDefault?a.button:({1:0,2:2,4:1})[a.button]},document.documentElement.setCapture?b.capture=function(a,c,d){function f(e){c&&c(e),d&&d(),b.removeListener(a,\"mousemove\",c),b.removeListener(a,\"mouseup\",f),b.removeListener(a,\"losecapture\",f),a.releaseCapture()}function e(a){c(a);return b.stopPropagation(a)}b.addListener(a,\"mousemove\",c),b.addListener(a,\"mouseup\",f),b.addListener(a,\"losecapture\",f),a.setCapture()}:b.capture=function(a,b,c){function e(a){b&&b(a),c&&c(),document.removeEventListener(\"mousemove\",d,!0),document.removeEventListener(\"mouseup\",e,!0),a.stopPropagation()}function d(a){b(a),a.stopPropagation()}document.addEventListener(\"mousemove\",d,!0),document.addEventListener(\"mouseup\",e,!0)},b.addMouseWheelListener=function(a,c){var d=function(a){a.wheelDelta!==undefined?a.wheelDeltaX!==undefined?(a.wheelX=-a.wheelDeltaX/8,a.wheelY=-a.wheelDeltaY/8):(a.wheelX=0,a.wheelY=-a.wheelDelta/8):a.axis&&a.axis==a.HORIZONTAL_AXIS?(a.wheelX=(a.detail||0)*5,a.wheelY=0):(a.wheelX=0,a.wheelY=(a.detail||0)*5),c(a)};b.addListener(a,\"DOMMouseScroll\",d),b.addListener(a,\"mousewheel\",d)},b.addMultiMouseDownListener=function(a,c,d,f,g){var h=0,i,j,k=function(a){h+=1,h==1&&(i=a.clientX,j=a.clientY,setTimeout(function(){h=0},f||600));if(b.getButton(a)!=c||Math.abs(a.clientX-i)>5||Math.abs(a.clientY-j)>5)h=0;h==d&&(h=0,g(a));return b.preventDefault(a)};b.addListener(a,\"mousedown\",k),e.isIE&&b.addListener(a,\"dblclick\",k)},b.addCommandKeyListener=function(a,c){var d=b.addListener;if(e.isOldGecko){var f=null;d(a,\"keydown\",function(a){f=a.keyCode}),d(a,\"keypress\",function(a){return g(c,a,f)})}else{var h=null;d(a,\"keydown\",function(a){h=a.keyIdentifier||a.keyCode;return g(c,a,a.keyCode)}),e.isMac&&e.isOpera&&d(a,\"keypress\",function(a){var b=a.keyIdentifier||a.keyCode;if(h!==b)return g(c,a,a.keyCode);h=null})}}}),define(\"pilot/dom\",function(a,b,c){var d=\"http://www.w3.org/1999/xhtml\";b.createElement=function(a,b){return document.createElementNS?document.createElementNS(b||d,a):document.createElement(a)},b.setText=function(a,b){a.innerText!==undefined&&(a.innerText=b),a.textContent!==undefined&&(a.textContent=b)},document.documentElement.classList?(b.hasCssClass=function(a,b){return a.classList.contains(b)},b.addCssClass=function(a,b){a.classList.add(b)},b.removeCssClass=function(a,b){a.classList.remove(b)},b.toggleCssClass=function(a,b){return a.classList.toggle(b)}):(b.hasCssClass=function(a,b){var c=a.className.split(/\\s+/g);return c.indexOf(b)!==-1},b.addCssClass=function(a,c){b.hasCssClass(a,c)||(a.className+=\" \"+c)},b.removeCssClass=function(a,b){var c=a.className.split(/\\s+/g);while(!0){var d=c.indexOf(b);if(d==-1)break;c.splice(d,1)}a.className=c.join(\" \")},b.toggleCssClass=function(a,b){var c=a.className.split(/\\s+/g),d=!0;while(!0){var e=c.indexOf(b);if(e==-1)break;d=!1,c.splice(e,1)}d&&c.push(b),a.className=c.join(\" \");return d}),b.setCssClass=function(a,c,d){d?b.addCssClass(a,c):b.removeCssClass(a,c)},b.importCssString=function(a,b){b=b||document;if(b.createStyleSheet){var c=b.createStyleSheet();c.cssText=a}else{var e=b.createElementNS?b.createElementNS(d,\"style\"):b.createElement(\"style\");e.appendChild(b.createTextNode(a));var f=b.getElementsByTagName(\"head\")[0]||b.documentElement;f.appendChild(e)}},b.getInnerWidth=function(a){return parseInt(b.computedStyle(a,\"paddingLeft\"))+parseInt(b.computedStyle(a,\"paddingRight\"))+a.clientWidth},b.getInnerHeight=function(a){return parseInt(b.computedStyle(a,\"paddingTop\"))+parseInt(b.computedStyle(a,\"paddingBottom\"))+a.clientHeight},window.pageYOffset!==undefined?(b.getPageScrollTop=function(){return window.pageYOffset},b.getPageScrollLeft=function(){return window.pageXOffset}):(b.getPageScrollTop=function(){return document.body.scrollTop},b.getPageScrollLeft=function(){return document.body.scrollLeft}),b.computedStyle=function(a,b){return window.getComputedStyle?(window.getComputedStyle(a,\"\")||{})[b]||\"\":a.currentStyle[b]},b.scrollbarWidth=function(){var a=b.createElement(\"p\");a.style.width=\"100%\",a.style.height=\"200px\";var c=b.createElement(\"div\"),d=c.style;d.position=\"absolute\",d.left=\"-10000px\",d.overflow=\"hidden\",d.width=\"200px\",d.height=\"150px\",c.appendChild(a);var e=document.body||document.documentElement;e.appendChild(c);var f=a.offsetWidth;d.overflow=\"scroll\";var g=a.offsetWidth;f==g&&(g=c.clientWidth),e.removeChild(c);return f-g},b.setInnerHtml=function(a,b){var c=a.cloneNode(!1);c.innerHTML=b,a.parentNode.replaceChild(c,a);return c},b.setInnerText=function(a,b){document.body&&\"textContent\"in document.body?a.textContent=b:a.innerText=b},b.getInnerText=function(a){return document.body&&\"textContent\"in document.body?a.textContent:a.innerText||a.textContent||\"\"},b.getParentWindow=function(a){return a.defaultView||a.parentWindow},b.getSelectionStart=function(a){var b;try{b=a.selectionStart||0}catch(c){b=0}return b},b.setSelectionStart=function(a,b){return a.selectionStart=b},b.getSelectionEnd=function(a){var b;try{b=a.selectionEnd||0}catch(c){b=0}return b},b.setSelectionEnd=function(a,b){return a.selectionEnd=b}}),define(\"ace/keyboard/textinput\",function(a,b,c){var d=a(\"pilot/event\"),e=a(\"pilot/useragent\"),f=a(\"pilot/dom\"),g=function(a,b){function k(a){if(!i){var d=a||c.value;d&&(d.charCodeAt(d.length-1)==g.charCodeAt(0)?(d=d.slice(0,-1),d&&b.onTextInput(d)):b.onTextInput(d))}i=!1,c.value=g,c.select()}var c=f.createElement(\"textarea\");c.style.left=\"-10000px\",a.appendChild(c);var g=String.fromCharCode(0);k();var h=!1,i=!1,j=\"\",l=function(a){e.isIE&&c.value.charCodeAt(0)>128||setTimeout(function(){h||k()},0)},m=function(a){h=!0,e.isIE||(k(),c.value=\"\"),b.onCompositionStart(),e.isGecko||setTimeout(n,0)},n=function(){!h||b.onCompositionUpdate(c.value)},o=function(){h=!1,b.onCompositionEnd(),setTimeout(function(){k()},0)},p=function(a){i=!0;var d=b.getCopyText();d?c.value=d:a.preventDefault(),c.select(),setTimeout(function(){k()},0)},q=function(a){i=!0;var d=b.getCopyText();d?(c.value=d,b.onCut()):a.preventDefault(),c.select(),setTimeout(function(){k()},0)};d.addCommandKeyListener(c,b.onCommandKey.bind(b)),d.addListener(c,\"keypress\",l);if(e.isIE){var r={13:1,27:1};d.addListener(c,\"keyup\",function(a){h&&(!c.value||r[a.keyCode])&&setTimeout(o,0);(c.value.charCodeAt(0)|0)<129||(h?n():m())})}d.addListener(c,\"textInput\",l),d.addListener(c,\"paste\",function(a){a.clipboardData&&a.clipboardData.getData?(k(a.clipboardData.getData(\"text/plain\")),a.preventDefault()):l()}),e.isIE||d.addListener(c,\"propertychange\",l),e.isIE?(d.addListener(c,\"beforecopy\",function(a){var c=b.getCopyText();c?clipboardData.setData(\"Text\",c):a.preventDefault()}),d.addListener(a,\"keydown\",function(a){if(a.ctrlKey&&a.keyCode==88){var c=b.getCopyText();c&&(clipboardData.setData(\"Text\",c),b.onCut()),d.preventDefault(a)}})):(d.addListener(c,\"copy\",p),d.addListener(c,\"cut\",q)),d.addListener(c,\"compositionstart\",m),e.isGecko&&d.addListener(c,\"text\",n),e.isWebKit&&d.addListener(c,\"keyup\",n),d.addListener(c,\"compositionend\",o),d.addListener(c,\"blur\",function(){b.onBlur()}),d.addListener(c,\"focus\",function(){b.onFocus(),c.select()}),this.focus=function(){b.onFocus(),c.select(),c.focus()},this.blur=function(){c.blur()},this.getElement=function(){return c},this.onContextMenu=function(a,b){a&&(j||(j=c.style.cssText),c.style.cssText=\"position:fixed; z-index:1000;left:\"+(a.x-2)+\"px; top:\"+(a.y-2)+\"px;\"),b&&(c.value=\"\")},this.onContextMenuClose=function(){setTimeout(function(){j&&(c.style.cssText=j,j=\"\"),k()},0)}};b.TextInput=g}),define(\"ace/mouse_handler\",function(a,b,c){var d=a(\"pilot/event\"),e=a(\"pilot/dom\"),f=0,g=1,h=2,i=250,j=5,k=function(a){this.editor=a,d.addListener(a.container,\"mousedown\",function(b){a.focus();return d.preventDefault(b)}),d.addListener(a.container,\"selectstart\",function(a){return d.preventDefault(a)});var b=a.renderer.getMouseEventTarget();d.addListener(b,\"mousedown\",this.onMouseDown.bind(this)),d.addMultiMouseDownListener(b,0,2,500,this.onMouseDoubleClick.bind(this)),d.addMultiMouseDownListener(b,0,3,600,this.onMouseTripleClick.bind(this)),d.addMultiMouseDownListener(b,0,4,600,this.onMouseQuadClick.bind(this)),d.addMouseWheelListener(b,this.onMouseWheel.bind(this))};(function(){this.$scrollSpeed=1,this.setScrollSpeed=function(a){this.$scrollSpeed=a},this.getScrollSpeed=function(){return this.$scrollSpeed},this.$getEventPosition=function(a){var b=d.getDocumentX(a),c=d.getDocumentY(a),e=this.editor.renderer.screenToTextCoordinates(b,c);e.row=Math.max(0,Math.min(e.row,this.editor.session.getLength()-1));return e},this.$distance=function(a,b,c,d){return Math.sqrt(Math.pow(c-a,2)+Math.pow(d-b,2))},this.onMouseDown=function(a){function C(b){a.shiftKey?l.selection.selectToPosition(b):m.$clickSelection||(l.moveCursorToPosition(b),l.selection.clearSelection(b.row,b.column)),p=g}var b=d.getDocumentX(a),c=d.getDocumentY(a),k=this.$getEventPosition(a),l=this.editor,m=this,n=l.getSelectionRange(),o=n.isEmpty(),p=f,q=!1,r=d.getButton(a);if(r!==0)o&&l.moveCursorToPosition(k),r==2&&(l.textInput.onContextMenu({x:b,y:c},o),d.capture(l.container,function(){},l.textInput.onContextMenuClose));else{q=!l.getReadOnly()&&!o&&n.contains(k.row,k.column),q||C(k),l.renderer.scrollCursorIntoView();var s,t,u=l.getOverwrite(),v=(new Date).getTime(),w,x,y=function(a){s=d.getDocumentX(a),t=d.getDocumentY(a)},z=function(){clearInterval(F),p==f?C(k):p==h&&A(),m.$clickSelection=null,p=f},A=function(){e.removeCssClass(l.container,\"ace_dragging\"),l.session.removeMarker(dragSelectionMarker),m.$clickSelection||w||(l.moveCursorToPosition(k),l.selection.clearSelection(k.row,k.column));if(!!w){if(x.contains(w.row,w.column)){w=null;return}l.clearSelection();var a=l.moveText(x,w);if(!a){w=null;return}l.selection.setSelectionRange(a)}},B=function(){if(s!==undefined&&t!==undefined){if(p==f){var a=m.$distance(b,c,s,t),d=(new Date).getTime();if(a>j){p=g;var k=l.renderer.screenToTextCoordinates(s,t);k.row=Math.max(0,Math.min(k.row,l.session.getLength()-1)),C(k)}else if(d-v>i){p=h,x=l.getSelectionRange();var n=l.getSelectionStyle();dragSelectionMarker=l.session.addMarker(x,\"ace_selection\",n),l.clearSelection(),e.addCssClass(l.container,\"ace_dragging\")}}p==h?E():p==g&&D()}},D=function(){var a=l.renderer.screenToTextCoordinates(s,t);a.row=Math.max(0,Math.min(a.row,l.session.getLength()-1));if(m.$clickSelection)if(m.$clickSelection.contains(a.row,a.column))l.selection.setSelectionRange(m.$clickSelection);else{if(m.$clickSelection.compare(a.row,a.column)==-1)var b=m.$clickSelection.end;else var b=m.$clickSelection.start;l.selection.setSelectionAnchor(b.row,b.column),l.selection.selectToPosition(a)}else l.selection.selectToPosition(a);l.renderer.scrollCursorIntoView()},E=function(){w=l.renderer.screenToTextCoordinates(s,t),w.row=Math.max(0,Math.min(w.row,l.session.getLength()-1)),l.moveCursorToPosition(w)};d.capture(l.container,y,z);var F=setInterval(B,20);return d.preventDefault(a)}},this.onMouseDoubleClick=function(a){var b=this.$getEventPosition(a);this.editor.moveCursorToPosition(b),this.editor.selection.selectWord(),this.$clickSelection=this.editor.getSelectionRange()},this.onMouseTripleClick=function(a){var b=this.$getEventPosition(a);this.editor.moveCursorToPosition(b),this.editor.selection.selectLine(),this.$clickSelection=this.editor.getSelectionRange()},this.onMouseQuadClick=function(a){this.editor.selectAll(),this.$clickSelection=this.editor.getSelectionRange()},this.onMouseWheel=function(a){var b=this.$scrollSpeed*2;this.editor.renderer.scrollBy(a.wheelX*b,a.wheelY*b);return d.preventDefault(a)}}).call(k.prototype),b.MouseHandler=k}),define(\"ace/keyboard/keybinding\",function(a,b,c){var d=a(\"pilot/useragent\"),e=a(\"pilot/keys\"),f=a(\"pilot/event\"),g=a(\"pilot/settings\").settings,h=a(\"pilot/canon\");a(\"ace/commands/default_commands\");var i=function(a){this.$editor=a,this.$data={},this.$keyboardHandler=null};(function(){this.setKeyboardHandler=function(a){this.$keyboardHandler!=a&&(this.$data={},this.$keyboardHandler=a)},this.getKeyboardHandler=function(){return this.$keyboardHandler},this.$callKeyboardHandler=function(a,b,c,d){var e={editor:this.$editor},g;this.$keyboardHandler&&(g=this.$keyboardHandler.handleKeyboard(this.$data,b,c,d,a));if(!g||!g.command)b!=0||d!=0?g={command:h.findKeyCommand(e,\"editor\",b,c)}:g={command:\"inserttext\",args:{text:c}};if(g){var i=h.exec(g.command,e,\"editor\",g.args);if(i)return f.stopEvent(a)}},this.onCommandKey=function(a,b,c){var d=e.keyCodeToString(c);this.$callKeyboardHandler(a,b,d,c)},this.onTextInput=function(a){this.$callKeyboardHandler({},0,a,0)}}).call(i.prototype),b.KeyBinding=i}),define(\"ace/commands/default_commands\",function(a,b,c){function f(a,b){return{win:a,mac:b,sender:\"editor\"}}var d=a(\"pilot/lang\"),e=a(\"pilot/canon\");e.addCommand({name:\"null\",exec:function(a,b,c){}}),e.addCommand({name:\"selectall\",bindKey:f(\"Ctrl-A\",\"Command-A\"),exec:function(a,b,c){a.editor.selectAll()}}),e.addCommand({name:\"removeline\",bindKey:f(\"Ctrl-D\",\"Command-D\"),exec:function(a,b,c){a.editor.removeLines()}}),e.addCommand({name:\"gotoline\",bindKey:f(\"Ctrl-L\",\"Command-L\"),exec:function(a,b,c){var d=parseInt(prompt(\"Enter line number:\"));isNaN(d)||a.editor.gotoLine(d)}}),e.addCommand({name:\"togglecomment\",bindKey:f(\"Ctrl-7\",\"Command-7\"),exec:function(a,b,c){a.editor.toggleCommentLines()}}),e.addCommand({name:\"findnext\",bindKey:f(\"Ctrl-K\",\"Command-G\"),exec:function(a,b,c){a.editor.findNext()}}),e.addCommand({name:\"findprevious\",bindKey:f(\"Ctrl-Shift-K\",\"Command-Shift-G\"),exec:function(a,b,c){a.editor.findPrevious()}}),e.addCommand({name:\"find\",bindKey:f(\"Ctrl-F\",\"Command-F\"),exec:function(a,b,c){var d=prompt(\"Find:\");a.editor.find(d)}}),e.addCommand({name:\"replace\",bindKey:f(\"Ctrl-R\",\"Command-Option-F\"),exec:function(a,b,c){var d=prompt(\"Find:\");if(!!d){var e=prompt(\"Replacement:\");if(!e)return;a.editor.replace(e,{needle:d})}}}),e.addCommand({name:\"replaceall\",bindKey:f(\"Ctrl-Shift-R\",\"Command-Shift-Option-F\"),exec:function(a,b,c){var d=prompt(\"Find:\");if(!!d){var e=prompt(\"Replacement:\");if(!e)return;a.editor.replaceAll(e,{needle:d})}}}),e.addCommand({name:\"undo\",bindKey:f(\"Ctrl-Z\",\"Command-Z\"),exec:function(a,b,c){a.editor.undo()}}),e.addCommand({name:\"redo\",bindKey:f(\"Ctrl-Shift-Z|Ctrl-Y\",\"Command-Shift-Z|Command-Y\"),exec:function(a,b,c){a.editor.redo()}}),e.addCommand({name:\"overwrite\",bindKey:f(\"Insert\",\"Insert\"),exec:function(a,b,c){a.editor.toggleOverwrite()}}),e.addCommand({name:\"copylinesup\",bindKey:f(\"Ctrl-Alt-Up\",\"Command-Option-Up\"),exec:function(a,b,c){a.editor.copyLinesUp()}}),e.addCommand({name:\"movelinesup\",bindKey:f(\"Alt-Up\",\"Option-Up\"),exec:function(a,b,c){a.editor.moveLinesUp()}}),e.addCommand({name:\"selecttostart\",bindKey:f(\"Alt-Shift-Up\",\"Command-Shift-Up\"),exec:function(a,b,c){a.editor.getSelection().selectFileStart()}}),e.addCommand({name:\"gotostart\",bindKey:f(\"Ctrl-Home|Ctrl-Up\",\"Command-Home|Command-Up\"),exec:function(a,b,c){a.editor.navigateFileStart()}}),e.addCommand({name:\"selectup\",bindKey:f(\"Shift-Up\",\"Shift-Up\"),exec:function(a,b,c){a.editor.getSelection().selectUp()}}),e.addCommand({name:\"golineup\",bindKey:f(\"Up\",\"Up|Ctrl-P\"),exec:function(a,b,c){a.editor.navigateUp(b.times)}}),e.addCommand({name:\"copylinesdown\",bindKey:f(\"Ctrl-Alt-Down\",\"Command-Option-Down\"),exec:function(a,b,c){a.editor.copyLinesDown()}}),e.addCommand({name:\"movelinesdown\",bindKey:f(\"Alt-Down\",\"Option-Down\"),exec:function(a,b,c){a.editor.moveLinesDown()}}),e.addCommand({name:\"selecttoend\",bindKey:f(\"Alt-Shift-Down\",\"Command-Shift-Down\"),exec:function(a,b,c){a.editor.getSelection().selectFileEnd()}}),e.addCommand({name:\"gotoend\",bindKey:f(\"Ctrl-End|Ctrl-Down\",\"Command-End|Command-Down\"),exec:function(a,b,c){a.editor.navigateFileEnd()}}),e.addCommand({name:\"selectdown\",bindKey:f(\"Shift-Down\",\"Shift-Down\"),exec:function(a,b,c){a.editor.getSelection().selectDown()}}),e.addCommand({name:\"golinedown\",bindKey:f(\"Down\",\"Down|Ctrl-N\"),exec:function(a,b,c){a.editor.navigateDown(b.times)}}),e.addCommand({name:\"selectwordleft\",bindKey:f(\"Ctrl-Shift-Left\",\"Option-Shift-Left\"),exec:function(a,b,c){a.editor.getSelection().selectWordLeft()}}),e.addCommand({name:\"gotowordleft\",bindKey:f(\"Ctrl-Left\",\"Option-Left\"),exec:function(a,b,c){a.editor.navigateWordLeft()}}),e.addCommand({name:\"selecttolinestart\",bindKey:f(\"Alt-Shift-Left\",\"Command-Shift-Left\"),exec:function(a,b,c){a.editor.getSelection().selectLineStart()}}),e.addCommand({name:\"gotolinestart\",bindKey:f(\"Alt-Left|Home\",\"Command-Left|Home|Ctrl-A\"),exec:function(a,b,c){a.editor.navigateLineStart()}}),e.addCommand({name:\"selectleft\",bindKey:f(\"Shift-Left\",\"Shift-Left\"),exec:function(a,b,c){a.editor.getSelection().selectLeft()}}),e.addCommand({name:\"gotoleft\",bindKey:f(\"Left\",\"Left|Ctrl-B\"),exec:function(a,b,c){a.editor.navigateLeft(b.times)}}),e.addCommand({name:\"selectwordright\",bindKey:f(\"Ctrl-Shift-Right\",\"Option-Shift-Right\"),exec:function(a,b,c){a.editor.getSelection().selectWordRight()}}),e.addCommand({name:\"gotowordright\",bindKey:f(\"Ctrl-Right\",\"Option-Right\"),exec:function(a,b,c){a.editor.navigateWordRight()}}),e.addCommand({name:\"selecttolineend\",bindKey:f(\"Alt-Shift-Right\",\"Command-Shift-Right\"),exec:function(a,b,c){a.editor.getSelection().selectLineEnd()}}),e.addCommand({name:\"gotolineend\",bindKey:f(\"Alt-Right|End\",\"Command-Right|End|Ctrl-E\"),exec:function(a,b,c){a.editor.navigateLineEnd()}}),e.addCommand({name:\"selectright\",bindKey:f(\"Shift-Right\",\"Shift-Right\"),exec:function(a,b,c){a.editor.getSelection().selectRight()}}),e.addCommand({name:\"gotoright\",bindKey:f(\"Right\",\"Right|Ctrl-F\"),exec:function(a,b,c){a.editor.navigateRight(b.times)}}),e.addCommand({name:\"selectpagedown\",bindKey:f(\"Shift-PageDown\",\"Shift-PageDown\"),exec:function(a,b,c){a.editor.selectPageDown()}}),e.addCommand({name:\"pagedown\",bindKey:f(null,\"PageDown\"),exec:function(a,b,c){a.editor.scrollPageDown()}}),e.addCommand({name:\"gotopagedown\",bindKey:f(\"PageDown\",\"Option-PageDown|Ctrl-V\"),exec:function(a,b,c){a.editor.gotoPageDown()}}),e.addCommand({name:\"selectpageup\",bindKey:f(\"Shift-PageUp\",\"Shift-PageUp\"),exec:function(a,b,c){a.editor.selectPageUp()}}),e.addCommand({name:\"pageup\",bindKey:f(null,\"PageUp\"),exec:function(a,b,c){a.editor.scrollPageUp()}}),e.addCommand({name:\"gotopageup\",bindKey:f(\"PageUp\",\"Option-PageUp\"),exec:function(a,b,c){a.editor.gotoPageUp()}}),e.addCommand({name:\"selectlinestart\",bindKey:f(\"Shift-Home\",\"Shift-Home\"),exec:function(a,b,c){a.editor.getSelection().selectLineStart()}}),e.addCommand({name:\"selectlineend\",bindKey:f(\"Shift-End\",\"Shift-End\"),exec:function(a,b,c){a.editor.getSelection().selectLineEnd()}}),e.addCommand({name:\"del\",bindKey:f(\"Delete\",\"Delete|Ctrl-D\"),exec:function(a,b,c){a.editor.removeRight()}}),e.addCommand({name:\"backspace\",bindKey:f(\"Ctrl-Backspace|Command-Backspace|Option-Backspace|Shift-Backspace|Backspace\",\"Ctrl-Backspace|Command-Backspace|Shift-Backspace|Backspace|Ctrl-H\"),exec:function(a,b,c){a.editor.removeLeft()}}),e.addCommand({name:\"removetolinestart\",bindKey:f(null,\"Option-Backspace\"),exec:function(a,b,c){a.editor.removeToLineStart()}}),e.addCommand({name:\"removetolineend\",bindKey:f(null,\"Ctrl-K\"),exec:function(a,b,c){a.editor.removeToLineEnd()}}),e.addCommand({name:\"removewordleft\",bindKey:f(null,\"Alt-Backspace|Ctrl-Alt-Backspace\"),exec:function(a,b,c){a.editor.removeWordLeft()}}),e.addCommand({name:\"removewordright\",bindKey:f(null,\"Alt-Delete\"),exec:function(a,b,c){a.editor.removeWordRight()}}),e.addCommand({name:\"outdent\",bindKey:f(\"Shift-Tab\",\"Shift-Tab\"),exec:function(a,b,c){a.editor.blockOutdent()}}),e.addCommand({name:\"indent\",bindKey:f(\"Tab\",\"Tab\"),exec:function(a,b,c){a.editor.indent()}}),e.addCommand({name:\"inserttext\",exec:function(a,b,c){a.editor.insert(d.stringRepeat(b.text||\"\",b.times||1))}}),e.addCommand({name:\"splitline\",bindKey:f(null,\"Ctrl-O\"),exec:function(a,b,c){a.editor.splitLine()}}),e.addCommand({name:\"transposeletters\",bindKey:f(\"Ctrl-T\",\"Ctrl-T\"),exec:function(a,b,c){a.editor.transposeLetters()}})}),define(\"ace/edit_session\",function(a,b,c){var d=a(\"pilot/oop\"),e=a(\"pilot/lang\"),f=a(\"pilot/event_emitter\").EventEmitter,g=a(\"ace/selection\").Selection,h=a(\"ace/mode/text\").Mode,j=a(\"ace/range\").Range,k=a(\"ace/document\").Document,l=function(a,b){this.$modified=!0,this.$breakpoints=[],this.$frontMarkers={},this.$backMarkers={},this.$markerId=1,this.$wrapData=[],a instanceof k?this.setDocument(a):this.setDocument(new k(a)),this.selection=new g(this),b&&this.setMode(b)};(function(){d.implement(this,f),this.setDocument=function(a){if(this.doc)throw new Error(\"Document is already set\");this.doc=a,a.on(\"change\",this.onChange.bind(this))},this.getDocument=function(){return this.doc},this.onChange=function(a){var b=a.data;this.$modified=!0,!this.$fromUndo&&this.$undoManager&&!b.ignore&&(this.$deltas.push(b),this.$informUndoManager.schedule()),this.$updateWrapDataOnChange(a),this._dispatchEvent(\"change\",a)},this.setValue=function(a){this.doc.setValue(a),this.$deltas=[],this.getUndoManager().reset()},this.getValue=this.toString=function(){return this.doc.getValue()},this.getSelection=function(){return this.selection},this.setUndoManager=function(a){this.$undoManager=a,this.$deltas=[],this.$informUndoManager&&this.$informUndoManager.cancel();if(a){var b=this;this.$informUndoManager=e.deferredCall(function(){b.$deltas.length>0&&a.execute({action:\"aceupdate\",args:[b.$deltas,b]}),b.$deltas=[]})}},this.$defaultUndoManager={undo:function(){},redo:function(){},reset:function(){}},this.getUndoManager=function(){return this.$undoManager||this.$defaultUndoManager},this.getTabString=function(){return this.getUseSoftTabs()?e.stringRepeat(\" \",this.getTabSize()):\"\\t\"},this.$useSoftTabs=!0,this.setUseSoftTabs=function(a){this.$useSoftTabs!==a&&(this.$useSoftTabs=a)},this.getUseSoftTabs=function(){return this.$useSoftTabs},this.$tabSize=4,this.setTabSize=function(a){!isNaN(a)&&this.$tabSize!==a&&(this.$modified=!0,this.$tabSize=a,this._dispatchEvent(\"changeTabSize\"))},this.getTabSize=function(){return this.$tabSize},this.isTabStop=function(a){return this.$useSoftTabs&&a.column%this.$tabSize==0},this.$overwrite=!1,this.setOverwrite=function(a){this.$overwrite!=a&&(this.$overwrite=a,this._dispatchEvent(\"changeOverwrite\"))},this.getOverwrite=function(){return this.$overwrite},this.toggleOverwrite=function(){this.setOverwrite(!this.$overwrite)},this.getBreakpoints=function(){return this.$breakpoints},this.setBreakpoints=function(a){this.$breakpoints=[];for(var b=0;b<a.length;b++)this.$breakpoints[a[b]]=!0;this._dispatchEvent(\"changeBreakpoint\",{})},this.clearBreakpoints=function(){this.$breakpoints=[],this._dispatchEvent(\"changeBreakpoint\",{})},this.setBreakpoint=function(a){this.$breakpoints[a]=!0,this._dispatchEvent(\"changeBreakpoint\",{})},this.clearBreakpoint=function(a){delete this.$breakpoints[a],this._dispatchEvent(\"changeBreakpoint\",{})},this.getBreakpoints=function(){return this.$breakpoints},this.addMarker=function(a,b,c,d){var e=this.$markerId++,f={range:a,type:c||\"line\",renderer:typeof c==\"function\"?c:null,clazz:b,inFront:!!d};d?(this.$frontMarkers[e]=f,this._dispatchEvent(\"changeFrontMarker\")):(this.$backMarkers[e]=f,this._dispatchEvent(\"changeBackMarker\"));return e},this.removeMarker=function(a){var b=this.$frontMarkers[a]||this.$backMarkers[a];if(!!b){var c=b.inFront?this.$frontMarkers:this.$backMarkers;b&&(delete c[a],this._dispatchEvent(b.inFront?\"changeFrontMarker\":\"changeBackMarker\"))}},this.getMarkers=function(a){return a?this.$frontMarkers:this.$backMarkers},this.setAnnotations=function(a){this.$annotations={};for(var b=0;b<a.length;b++){var c=a[b],d=c.row;this.$annotations[d]?this.$annotations[d].push(c):this.$annotations[d]=[c]}this._dispatchEvent(\"changeAnnotation\",{})},this.getAnnotations=function(){return this.$annotations},this.clearAnnotations=function(){this.$annotations={},this._dispatchEvent(\"changeAnnotation\",{})},this.$detectNewLine=function(a){var b=a.match(/^.*?(\\r?\\n)/m);b?this.$autoNewLine=b[1]:this.$autoNewLine=\"\\n\"},this.tokenRe=/^[\\w\\d]+/g,this.nonTokenRe=/^(?:[^\\w\\d]|[\\u3040-\\u309F]|[\\u30A0-\\u30FF]|[\\u4E00-\\u9FFF\\uF900-\\uFAFF\\u3400-\\u4DBF])+/g,this.getWordRange=function(a,b){var c=this.getLine(a),d=!1;b>0&&(d=!!c.charAt(b-1).match(this.tokenRe)),d||(d=!!c.charAt(b).match(this.tokenRe));var e=d?this.tokenRe:this.nonTokenRe,f=b;if(f>0){do f--;while(f>=0&&c.charAt(f).match(e));f++}var g=b;while(g<c.length&&c.charAt(g).match(e))g++;return new j(a,f,a,g)},this.setNewLineMode=function(a){this.doc.setNewLineMode(a)},this.getNewLineMode=function(){return this.doc.getNewLineMode()},this.$useWorker=!0,this.setUseWorker=function(a){this.$useWorker!=a&&(a&&!this.$worker&&window.Worker&&(this.$worker=mode.createWorker(this)),!a&&this.$worker&&(this.$worker.terminate(),this.$worker=null))},this.getUseWorker=function(){return this.$useWorker},this.$mode=null,this.setMode=function(b){this.$mode!==b&&(this.$worker&&this.$worker.terminate(),this.$useWorker&&window.Worker&&!a.noWorker?this.$worker=b.createWorker(this):this.$worker=null,this.$mode=b,this._dispatchEvent(\"changeMode\"))},this.getMode=function(){this.$mode||(this.$mode=new h);return this.$mode},this.$scrollTop=0,this.setScrollTopRow=function(a){this.$scrollTop!==a&&(this.$scrollTop=a,this._dispatchEvent(\"changeScrollTop\"))},this.getScrollTopRow=function(){return this.$scrollTop},this.getWidth=function(){this.$computeWidth();return this.width},this.getScreenWidth=function(){this.$computeWidth();return this.screenWidth},this.$computeWidth=function(a){if(this.$modified||a){this.$modified=!1;var b=this.doc.getAllLines(),c=0,d=0,e=this.getTabSize();for(var f=0;f<b.length;f++){var g=b[f].length;c=Math.max(c,g),b[f].replace(/\\t/g,function(a){g+=e-1;return a}),d=Math.max(d,g)}this.width=c,this.$useWrapMode?this.screenWidth=this.$wrapLimit:this.screenWidth=d}},this.getLine=function(a){return this.doc.getLine(a)},this.getDisplayLine=function(a){var b=Array(this.getTabSize()+1).join(\" \");return this.doc.getLine(a).replace(/\\t/g,b)},this.getLines=function(a,b){return this.doc.getLines(a,b)},this.getLength=function(){return this.doc.getLength()},this.getTextRange=function(a){return this.doc.getTextRange(a)},this.findMatchingBracket=function(a){if(a.column==0)return null;var b=this.getLine(a.row).charAt(a.column-1);if(b==\"\")return null;var c=b.match(/([\\(\\[\\{])|([\\)\\]\\}])/);if(!c)return null;return c[1]?this.$findClosingBracket(c[1],a):this.$findOpeningBracket(c[2],a)},this.$brackets={\")\":\"(\",\"(\":\")\",\"]\":\"[\",\"[\":\"]\",\"{\":\"}\",\"}\":\"{\"},this.$findOpeningBracket=function(a,b){var c=this.$brackets[a],d=b.column-2,e=b.row,f=1,g=this.getLine(e);while(!0){while(d>=0){var h=g.charAt(d);if(h==c){f-=1;if(f==0)return{row:e,column:d}}else h==a&&(f+=1);d-=1}e-=1;if(e<0)break;var g=this.getLine(e),d=g.length-1}return null},this.$findClosingBracket=function(a,b){var c=this.$brackets[a],d=b.column,e=b.row,f=1,g=this.getLine(e),h=this.getLength();while(!0){while(d<g.length){var i=g.charAt(d);if(i==c){f-=1;if(f==0)return{row:e,column:d}}else i==a&&(f+=1);d+=1}e+=1;if(e>=h)break;var g=this.getLine(e),d=0}return null},this.insert=function(a,b){return this.doc.insert(a,b)},this.remove=function(a){return this.doc.remove(a)},this.undoChanges=function(a){!a.length||(this.$fromUndo=!0,this.doc.revertDeltas(a),this.$fromUndo=!1,this.$setUndoSelection(a,!0))},this.redoChanges=function(a){!a.length||(this.$fromUndo=!0,this.doc.applyDeltas(a),this.$fromUndo=!1,this.$setUndoSelection(a,!1))},this.$setUndoSelection=function(a,b){b&&(a=a.map(function(a){var b={range:a.range};a.action==\"insertText\"||a.action==\"insertLines\"?b.action=\"removeText\":b.action=\"insertText\";return b}).reverse());var c=[{}];for(var d=0;d<a.length;d++){var e=a[d],f=e.action==\"insertText\"||e.action==\"insertLines\",g=c[c.length-1];g.isInsert!==f?c.push({isInsert:f,start:f?e.range.start:e.range.end,end:f?e.range.end:e.range.start}):f?g.end=e.range.end:g.start=e.range.start}this.selection.clearSelection();var g=c[c.length-1];g.isInsert?this.selection.setSelectionRange(j.fromPoints(g.start,g.end)):this.selection.moveCursorToPosition(g.end)},this.replace=function(a,b){return this.doc.replace(a,b)},this.moveText=function(a,b){var c=this.getTextRange(a);this.remove(a);var d=b.row,e=b.column;!a.isMultiLine()&&a.start.row==d&&a.end.column<e&&(e-=c.length);if(a.isMultiLine()&&a.end.row<d){var f=this.doc.$split(c);d-=f.length-1}var g=d+a.end.row-a.start.row,h=a.isMultiLine()?a.end.column:e+a.end.column-a.start.column,i=new j(d,e,g,h);this.insert(i.start,c);return i},this.indentRows=function(a,b,c){c=c.replace(/\\t/g,this.getTabString());for(var d=a;d<=b;d++)this.insert({row:d,column:0},c)},this.outdentRows=function(a){var b=a.collapseRows(),c=new j(0,0,0,0),d=this.getTabSize();for(var e=b.start.row;e<=b.end.row;++e){var f=this.getLine(e);c.start.row=e,c.end.row=e;for(var g=0;g<d;++g)if(f.charAt(g)!=\" \")break;g<d&&f.charAt(g)==\"\\t\"?(c.start.column=g,c.end.column=g+1):(c.start.column=0,c.end.column=g),this.remove(c)}},this.moveLinesUp=function(a,b){if(a<=0)return 0;var c=this.doc.removeLines(a,b);this.doc.insertLines(a-1,c);return-1},this.moveLinesDown=function(a,b){if(b>=this.doc.getLength()-1)return 0;var c=this.doc.removeLines(a,b);this.doc.insertLines(a+1,c);return 1},this.duplicateLines=function(a,b){var a=this.$clipRowToDocument(a),b=this.$clipRowToDocument(b),c=this.getLines(a,b);this.doc.insertLines(a,c);var d=b-a+1;return d},this.$clipRowToDocument=function(a){return Math.max(0,Math.min(a,this.doc.getLength()-1))},this.$wrapLimit=80,this.$useWrapMode=!1,this.$wrapLimitRange={min:null,max:null},this.setUseWrapMode=function(a){if(a!=this.$useWrapMode){this.$useWrapMode=a,this.$modified=!0;if(a){var b=this.getLength();this.$wrapData=[];for(i=0;i<b;i++)this.$wrapData.push([]);this.$updateWrapData(0,b-1)}this._dispatchEvent(\"changeWrapMode\")}},this.getUseWrapMode=function(){return this.$useWrapMode},this.setWrapLimitRange=function(a,b){if(this.$wrapLimitRange.min!==a||this.$wrapLimitRange.max!==b)this.$wrapLimitRange.min=a,this.$wrapLimitRange.max=b,this.$modified=!0,this._dispatchEvent(\"changeWrapMode\")},this.adjustWrapLimit=function(a){var b=this.$constrainWrapLimit(a);if(b!=this.$wrapLimit&&b>0){this.$wrapLimit=b,this.$modified=!0,this.$useWrapMode&&(this.$updateWrapData(0,this.getLength()-1),this._dispatchEvent(\"changeWrapLimit\"));return!0}return!1},this.$constrainWrapLimit=function(a){var b=this.$wrapLimitRange.min;b&&(a=Math.max(b,a));var c=this.$wrapLimitRange.max;c&&(a=Math.min(c,a));return Math.max(1,a)},this.getWrapLimit=function(){return this.$wrapLimit},this.getWrapLimitRange=function(){return{min:this.$wrapLimitRange.min,max:this.$wrapLimitRange.max}},this.$updateWrapDataOnChange=function(a){if(!!this.$useWrapMode){var b,c=a.data.action,d=a.data.range.start.row,e=a.data.range.end.row;c.indexOf(\"Lines\")!=-1?(c==\"insertLines\"?e=d+a.data.lines.length:e=d,b=a.data.lines.length):b=e-d;if(b!=0)if(c.indexOf(\"remove\")!=-1)this.$wrapData.splice(d,b),e=d;else{var f=[d,0];for(var g=0;g<b;g++)f.push([]);this.$wrapData.splice.apply(this.$wrapData,f)}this.$wrapData.length!=this.doc.$lines.length&&console.error(\"The length of doc.$lines and $wrapData have to be the same!\"),this.$updateWrapData(d,e)}},this.$updateWrapData=function(a,b){var c=this.doc.getAllLines(),d=this.getTabSize(),e=this.$wrapData,f=this.$wrapLimit;for(var g=a;g<=b;g++)e[g]=this.$computeWrapSplits(c[g],f,d)};var b=1,c=2,g=3,k=4,l=5;this.$computeWrapSplits=function(a,b,c){function j(a){var b=e.slice(h,a),f=b.length;b.join(\"\").replace(/4/g,function(a){f-=c-1}).replace(/2/g,function(a){f-=1}),i+=f,d.push(i),h=a}a=a.trimRight();if(a.length==0)return[];var c=this.getTabSize(),d=[],e=this.$getDisplayTokens(a),f=e.length,h=0,i=0;while(f-h>b){var k=h+b;if(e[k]>=g){while(e[k]>=g)k++;j(k)}else{for(k;k!=h-1;k--)if(e[k]>=g){k++;break}k>h?j(k):j(h+b)}}return d},this.$getDisplayTokens=function(a){var d=[],e=this.getTabSize();for(var f=0;f<a.length;f++){var h=a.charCodeAt(f);if(h==9){d.push(k);for(var i=1;i<e;i++)d.push(l)}else h==32?d.push(g):h>=12352&&h<=12447||h>=12448&&h<=12543||h>=19968&&h<=40959||h>=63744&&h<=64255||h>=13312&&h<=19903?d.push(b,c):d.push(b)}return d},this.$getStringScreenWidth=function(a){var b=0,c=this.getTabSize();for(var d=0;d<a.length;d++){var e=a.charCodeAt(d);e==9?b+=c:e>=12352&&e<=12447||e>=12448&&e<=12543||e>=19968&&e<=40959||e>=63744&&e<=64255||e>=13312&&e<=19903?b+=2:b+=1}return b},this.getRowHeight=function(a,b){var c;!this.$useWrapMode||!this.$wrapData[b]?c=1:c=this.$wrapData[b].length+1;return c*a.lineHeight},this.getScreenLastRowColumn=function(a,b){if(!this.$useWrapMode)return this.$getStringScreenWidth(this.getLine(a));var c=this.$screenToDocumentRow(a),d=c[0],e=c[1],f,g;this.$wrapData[d][e]?(f=this.$wrapData[d][e-1]||0,g=this.$wrapData[d][e],b&&g--):(g=this.getLine(d).length,f=this.$wrapData[d][e-1]||0);return b?g:this.$getStringScreenWidth(this.getLine(d).substring(f,g))},this.getDocumentLastRowColumn=function(a,b){if(!this.$useWrapMode)return this.getLine(a).length;var c=this.documentToScreenRow(a,b);return this.getScreenLastRowColumn(c,!0)},this.getScreenFirstRowColumn=function(a){if(!this.$useWrapMode)return 0;var b=this.$screenToDocumentRow(a),c=b[0],d=b[1];return this.$wrapData[c][d-1]||0},this.getRowSplitData=function(a){return this.$useWrapMode?this.$wrapData[a]:undefined},this.$screenToDocumentRow=function(a){if(!this.$useWrapMode)return[a,0];var b=this.$wrapData,c=this.getLength(),d=0;while(d<c&&a>=b[d].length+1)a-=b[d].length+1,d++;return[d,a]},this.screenToDocumentRow=function(a){return this.$screenToDocumentRow(a)[0]},this.screenToDocumentColumn=function(a,b){return this.screenToDocumentPosition(a,b).column},this.screenToDocumentPosition=function(a,b){var c,d,e,f=b,g=this.getLength();if(!this.$useWrapMode)d=a>=g?g-1:a<0?0:a,a=0,e=0,c=this.getLine(d);else{var h=this.$wrapData,d=0;while(d<g&&a>=h[d].length+1)a-=h[d].length+1,d++;d>=g&&(d=g-1,a=h[d].length),e=h[d][a-1]||0,c=this.getLine(d).substring(e)}var i=this.getTabSize();for(var j=0;j<c.length;j++){var k=c.charCodeAt(j);if(f>0)e+=1,k==9?f>=i?f-=i:(f=0,e-=1):k>=12352&&k<=12447||k>=12448&&k<=12543||k>=19968&&k<=40959||k>=63744&&k<=64255||k>=13312&&k<=19903?f>=2?f-=2:(f=0,e-=1):f-=1;else break}this.$useWrapMode?(b=h[d][a],e>=b&&(e=b-1)):c&&(e=Math.min(e,c.length));return{row:d,column:e}},this.documentToScreenColumn=function(a,b){return this.documentToScreenPosition(a,b).column},this.$documentToScreenRow=function(a,b){if(!this.$useWrapMode)return[a,0];var c=this.$wrapData,d=0;if(a>c.length-1)return[this.getScreenLength(),c.length==0?0:c[c.length-1].length-1];for(var e=0;e<a;e++)d+=c[e].length+1;var f=0;while(b>=c[a][f])d++,f++;return[d,f]},this.documentToScreenRow=function(a,b){return this.$documentToScreenRow(a,b)[0]},this.documentToScreenPosition=function(a,b){var c,d=this.getTabSize(),e;b!=null?e=a:(e=a.row,b=a.column);if(!this.$useWrapMode){c=this.getLine(e).substring(0,b),b=this.$getStringScreenWidth(c);return{row:e,column:b}}var f=this.$documentToScreenRow(e,b),g=f[0];if(e>=this.getLength())return{row:g,column:0};var h,i=this.$wrapData[e],j,k=f[1];c=this.getLine(e).substring(i[k-1]||0,b),j=this.$getStringScreenWidth(c);return{row:g,column:j}},this.getScreenLength=function(){if(!this.$useWrapMode)return this.getLength();var a=0;for(var b=0;b<this.$wrapData.length;b++)a+=this.$wrapData[b].length+1;return a}}).call(l.prototype),b.EditSession=l}),define(\"ace/selection\",function(a,b,c){var d=a(\"pilot/oop\"),e=a(\"pilot/lang\"),f=a(\"pilot/event_emitter\").EventEmitter,g=a(\"ace/range\").Range,h=function(a){this.session=a,this.doc=a.getDocument(),this.clearSelection(),this.selectionLead=this.doc.createAnchor(0,0),this.selectionAnchor=this.doc.createAnchor(0,0);var b=this;this.selectionLead.on(\"change\",function(a){b._dispatchEvent(\"changeCursor\"),b.$isEmpty||b._dispatchEvent(\"changeSelection\"),a.old.row==a.value.row&&b.$updateDesiredColumn()}),this.selectionAnchor.on(\"change\",function(){b.$isEmpty||b._dispatchEvent(\"changeSelection\")})};(function(){d.implement(this,f),this.isEmpty=function(){return this.$isEmpty||this.selectionAnchor.row==this.selectionLead.row&&this.selectionAnchor.column==this.selectionLead.column},this.isMultiLine=function(){if(this.isEmpty())return!1;return this.getRange().isMultiLine()},this.getCursor=function(){return this.selectionLead.getPosition()},this.setSelectionAnchor=function(a,b){this.selectionAnchor.setPosition(a,b),this.$isEmpty&&(this.$isEmpty=!1,this._dispatchEvent(\"changeSelection\"))},this.getSelectionAnchor=function(){return this.$isEmpty?this.getSelectionLead():this.selectionAnchor.getPosition()},this.getSelectionLead=function(){return this.selectionLead.getPosition()},this.shiftSelection=function(a){if(this.$isEmpty)this.moveCursorTo(this.selectionLead.row,this.selectionLead.column+a);else{var b=this.getSelectionAnchor(),c=this.getSelectionLead(),d=this.isBackwards();(!d||b.column!==0)&&this.setSelectionAnchor(b.row,b.column+a),(d||c.column!==0)&&this.$moveSelection(function(){this.moveCursorTo(c.row,c.column+a)})}},this.isBackwards=function(){var a=this.selectionAnchor,b=this.selectionLead;return a.row>b.row||a.row==b.row&&a.column>b.column},this.getRange=function(){var a=this.selectionAnchor,b=this.selectionLead;if(this.isEmpty())return g.fromPoints(b,b);return this.isBackwards()?g.fromPoints(b,a):g.fromPoints(a,b)},this.clearSelection=function(){this.$isEmpty||(this.$isEmpty=!0,this._dispatchEvent(\"changeSelection\"))},this.selectAll=function(){var a=this.doc.getLength()-1;this.setSelectionAnchor(a,this.doc.getLine(a).length),this.moveCursorTo(0,0)},this.setSelectionRange=function(a,b){b?(this.setSelectionAnchor(a.end.row,a.end.column),this.selectTo(a.start.row,a.start.column)):(this.setSelectionAnchor(a.start.row,a.start.column),this.selectTo(a.end.row,a.end.column)),this.$updateDesiredColumn()},this.$updateDesiredColumn=function(){var a=this.getCursor();this.$desiredColumn=this.session.documentToScreenColumn(a.row,a.column)},this.$moveSelection=function(a){var b=this.selectionLead;this.$isEmpty&&this.setSelectionAnchor(b.row,b.column),a.call(this)},this.selectTo=function(a,b){this.$moveSelection(function(){this.moveCursorTo(a,b)})},this.selectToPosition=function(a){this.$moveSelection(function(){this.moveCursorToPosition(a)})},this.selectUp=function(){this.$moveSelection(this.moveCursorUp)},this.selectDown=function(){this.$moveSelection(this.moveCursorDown)},this.selectRight=function(){this.$moveSelection(this.moveCursorRight)},this.selectLeft=function(){this.$moveSelection(this.moveCursorLeft)},this.selectLineStart=function(){this.$moveSelection(this.moveCursorLineStart)},this.selectLineEnd=function(){this.$moveSelection(this.moveCursorLineEnd)},this.selectFileEnd=function(){this.$moveSelection(this.moveCursorFileEnd)},this.selectFileStart=function(){this.$moveSelection(this.moveCursorFileStart)},this.selectWordRight=function(){this.$moveSelection(this.moveCursorWordRight)},this.selectWordLeft=function(){this.$moveSelection(this.moveCursorWordLeft)},this.selectWord=function(){var a=this.getCursor(),b=this.session.getWordRange(a.row,a.column);this.setSelectionRange(b)},this.selectLine=function(){this.setSelectionAnchor(this.selectionLead.row,0),this.$moveSelection(function(){this.moveCursorTo(this.selectionLead.row+1,0)})},this.moveCursorUp=function(){this.moveCursorBy(-1,0)},this.moveCursorDown=function(){this.moveCursorBy(1,0)},this.moveCursorLeft=function(){var a=this.selectionLead.getPosition();if(a.column==0)a.row>0&&this.moveCursorTo(a.row-1,this.doc.getLine(a.row-1).length);else{var b=this.session.getTabSize();this.session.isTabStop(a)&&this.doc.getLine(a.row).slice(a.column-b,a.column).split(\" \").length-1==b?this.moveCursorBy(0,-b):this.moveCursorBy(0,-1)}},this.moveCursorRight=function(){if(this.selectionLead.column==this.doc.getLine(this.selectionLead.row).length)this.selectionLead.row<this.doc.getLength()-1&&this.moveCursorTo(this.selectionLead.row+1,0);else{var a=this.session.getTabSize(),b=this.selectionLead;this.session.isTabStop(b)&&this.doc.getLine(b.row).slice(b.column,b.column+a).split(\" \").length-1==a?this.moveCursorBy(0,a):this.moveCursorBy(0,1)}},this.moveCursorLineStart=function(){var a=this.selectionLead.row,b=this.selectionLead.column,c=this.session.documentToScreenRow(a,b),d=this.session.getScreenFirstRowColumn(c),e=this.doc.getLine(a).slice(d,b),f=e.match(/^\\s*/);if(f[0].length==0){var g=this.session.getDocumentLastRowColumn(a,b);f=this.doc.getLine(a).substring(d,g).match(/^\\s*/),this.moveCursorTo(a,d+f[0].length)}else f[0].length>=b?this.moveCursorTo(a,d):this.moveCursorTo(a,d+f[0].length)},this.moveCursorLineEnd=function(){var a=this.selectionLead;this.moveCursorTo(a.row,this.session.getDocumentLastRowColumn(a.row,a.column))},this.moveCursorFileEnd=function(){var a=this.doc.getLength()-1,b=this.doc.getLine(a).length;this.moveCursorTo(a,b)},this.moveCursorFileStart=function(){this.moveCursorTo(0,0)},this.moveCursorWordRight=function(){var a=this.selectionLead.row,b=this.selectionLead.column,c=this.doc.getLine(a),d=c.substring(b),e;this.session.nonTokenRe.lastIndex=0,this.session.tokenRe.lastIndex=0;if(b==c.length)this.moveCursorRight();else{if(e=this.session.nonTokenRe.exec(d))b+=this.session.nonTokenRe.lastIndex,this.session.nonTokenRe.lastIndex=0;else if(e=this.session.tokenRe.exec(d))b+=this.session.tokenRe.lastIndex,this.session.tokenRe.lastIndex=0;this.moveCursorTo(a,b)}},this.moveCursorWordLeft=function(){var a=this.selectionLead.row,b=this.selectionLead.column,c=this.doc.getLine(a),d=e.stringReverse(c.substring(0,b)),f;this.session.nonTokenRe.lastIndex=0,this.session.tokenRe.lastIndex=0;if(b==0)this.moveCursorLeft();else{if(f=this.session.nonTokenRe.exec(d))b-=this.session.nonTokenRe.lastIndex,this.session.nonTokenRe.lastIndex=0;else if(f=this.session.tokenRe.exec(d))b-=this.session.tokenRe.lastIndex,this.session.tokenRe.lastIndex=0;this.moveCursorTo(a,b)}},this.moveCursorBy=function(a,b){if(this.session.getUseWrapMode()){var c=this.session.documentToScreenPosition(this.selectionLead.row,this.selectionLead.column),d=b==0&&this.$desiredColumn||c.column,e=this.session.screenToDocumentPosition(c.row+a,d);this.moveCursorTo(e.row,e.column+b,b==0)}else{var f=b==0&&this.$desiredColumn||this.selectionLead.column;this.moveCursorTo(this.selectionLead.row+a,f+b,b==0)}},this.moveCursorToPosition=function(a){this.moveCursorTo(a.row,a.column)},this.moveCursorTo=function(a,b,c){this.selectionLead.setPosition(a,b),c||this.$updateDesiredColumn(this.selectionLead.column)},this.moveCursorToScreen=function(a,b,c){if(this.session.getUseWrapMode()){var d=this.session.screenToDocumentPosition(a,b);a=d.row,b=d.column}this.moveCursorTo(a,b,c)}}).call(h.prototype),b.Selection=h}),define(\"ace/range\",function(a,b,c){var d=function(a,b,c,d){this.start={row:a,column:b},this.end={row:c,column:d}};(function(){this.toString=function(){return\"Range: [\"+this.start.row+\"/\"+this.start.column+\"] -> [\"+this.end.row+\"/\"+this.end.column+\"]\"},this.contains=function(a,b){return this.compare(a,b)==0},this.compare=function(a,b){if(!this.isMultiLine()&&a===this.start.row)return b<this.start.column?-1:b>this.end.column?1:0;if(a<this.start.row)return-1;if(a>this.end.row)return 1;if(this.start.row===a)return b>=this.start.column?0:-1;if(this.end.row===a)return b<=this.end.column?0:1;return 0},this.clipRows=function(a,b){if(this.end.row>b)var c={row:b+1,column:0};if(this.start.row>b)var e={row:b+1,column:0};if(this.start.row<a)var e={row:a,column:0};if(this.end.row<a)var c={row:a,column:0};return d.fromPoints(e||this.start,c||this.end)},this.extend=function(a,b){var c=this.compare(a,b);if(c==0)return this;if(c==-1)var e={row:a,column:b};else var f={row:a,column:b};return d.fromPoints(e||this.start,f||this.end)},this.isEmpty=function(){return this.start.row==this.end.row&&this.start.column==this.end.column},this.isMultiLine=function(){return this.start.row!==this.end.row},this.clone=function(){return d.fromPoints(this.start,this.end)},this.collapseRows=function(){return this.end.column==0?new d(this.start.row,0,Math.max(this.start.row,this.end.row-1),0):new d(this.start.row,0,this.end.row,0)},this.toScreenRange=function(a){var b=a.documentToScreenPosition(this.start),c=a.documentToScreenPosition(this.end);return new d(b.row,b.column,c.row,c.column)}}).call(d.prototype),d.fromPoints=function(a,b){return new d(a.row,a.column,b.row,b.column)},b.Range=d}),define(\"ace/mode/text\",function(a,b,c){var d=a(\"ace/tokenizer\").Tokenizer,e=a(\"ace/mode/text_highlight_rules\").TextHighlightRules,f=function(){this.$tokenizer=new d((new e).getRules())};(function(){this.getTokenizer=function(){return this.$tokenizer},this.toggleCommentLines=function(a,b,c,d){},this.getNextLineIndent=function(a,b,c){return\"\"},this.checkOutdent=function(a,b,c){return!1},this.autoOutdent=function(a,b,c){},this.$getIndent=function(a){var b=a.match(/^(\\s+)/);if(b)return b[1];return\"\"},this.createWorker=function(a){return null},this.highlightSelection=function(a){var b=a.session;b.$selectionOccurrences||(b.$selectionOccurrences=[]),b.$selectionOccurrences.length&&this.clearSelectionHighlight(a);var c=a.getSelectionRange();if(!c.isEmpty()&&!c.isMultiLine()){var d=c.start.column-1,e=c.end.column+1,f=b.getLine(c.start.row),g=f.length,h=f.substring(Math.max(d,0),Math.min(e,g));if(d>=0&&/^[\\w\\d]/.test(h)||e<=g&&/[\\w\\d]$/.test(h))return;h=f.substring(c.start.column,c.end.column);if(!/^[\\w\\d]+$/.test(h))return;var i=a.getCursorPosition(),j={wrap:!0,wholeWord:!0,caseSensitive:!0,needle:h},k=a.$search.getOptions();a.$search.set(j);var l=a.$search.findAll(b);l.forEach(function(a){if(!a.contains(i.row,i.column)){var c=b.addMarker(a,\"ace_selected_word\");b.$selectionOccurrences.push(c)}}),a.$search.set(k)}},this.clearSelectionHighlight=function(a){!a.session.$selectionOccurrences||(a.session.$selectionOccurrences.forEach(function(b){a.session.removeMarker(b)}),a.session.$selectionOccurrences=[])}}).call(f.prototype),b.Mode=f}),define(\"ace/tokenizer\",function(a,b,c){var d=function(a){this.rules=a,this.regExps={};for(var b in this.rules){var c=this.rules[b],d=c,e=[];for(var f=0;f<d.length;f++)e.push(d[f].regex);this.regExps[b]=new RegExp(\"(?:(\"+e.join(\")|(\")+\")|(.))\",\"g\")}};(function(){this.getLineTokens=function(a,b){var c=b,d=this.rules[c],e=this.regExps[c];e.lastIndex=0;var f,g=[],h=0,i={type:null,value:\"\"};while(f=e.exec(a)){var j=\"text\",k=f[0];for(var l=0;l<d.length;l++)if(f[l+1]){var m=d[l];typeof m.token==\"function\"?j=m.token(f[0]):j=m.token,m.next&&m.next!==c&&(c=m.next,d=this.rules[c],h=e.lastIndex,e=this.regExps[c],e.lastIndex=h);break}i.type!==j?(i.type&&g.push(i),i={type:j,value:k}):i.value+=k;if(h==a.length)break;h=e.lastIndex}i.type&&g.push(i);return{tokens:g,state:c}}}).call(d.prototype),b.Tokenizer=d}),define(\"ace/mode/text_highlight_rules\",function(a,b,c){var d=function(){this.$rules={start:[{token:\"empty_line\",regex:\"^$\"},{token:\"text\",regex:\".+\"}]}};(function(){this.addRules=function(a,b){for(var c in a){var d=a[c];for(var e=0;e<d.length;e++){var f=d[e];f.next?f.next=b+f.next:f.next=b+c}this.$rules[b+c]=d}},this.getRules=function(){return this.$rules}}).call(d.prototype),b.TextHighlightRules=d}),define(\"ace/document\",function(a,b,c){var d=a(\"pilot/oop\"),e=a(\"pilot/event_emitter\").EventEmitter,f=a(\"ace/range\").Range,g=a(\"ace/anchor\").Anchor,h=function(a){this.$lines=[],Array.isArray(a)?this.insertLines(0,a):a.length==0?this.$lines=[\"\"]:this.insert({row:0,column:0},a)};(function(){d.implement(this,e),this.setValue=function(a){var b=this.getLength();this.remove(new f(0,0,b,this.getLine(b-1).length)),this.insert({row:0,column:0},a)},this.getValue=function(){return this.getAllLines().join(this.getNewLineCharacter())},this.createAnchor=function(a,b){return new g(this,a,b)},\"aaa\".split(/a/).length==0?this.$split=function(a){return a.replace(/\\r\\n|\\r/g,\"\\n\").split(\"\\n\")}:this.$split=function(a){return a.split(/\\r\\n|\\r|\\n/)},this.$detectNewLine=function(a){var b=a.match(/^.*?(\\r?\\n)/m);b?this.$autoNewLine=b[1]:this.$autoNewLine=\"\\n\"},this.getNewLineCharacter=function(){switch(this.$newLineMode){case\"windows\":return\"\\r\\n\";case\"unix\":return\"\\n\";case\"auto\":return this.$autoNewLine}},this.$autoNewLine=\"\\n\",this.$newLineMode=\"auto\",this.setNewLineMode=function(a){this.$newLineMode!==a&&(this.$newLineMode=a)},this.getNewLineMode=function(){return this.$newLineMode},this.isNewLine=function(a){return a==\"\\r\\n\"||a==\"\\r\"||a==\"\\n\"},this.getLine=function(a){return this.getLines(a,a+1)[0]||\"\"},this.getLines=function(a,b){return this.$lines.slice(a,b+1)},this.getAllLines=function(){return this.getLines(0,this.getLength())},this.getLength=function(){return this.$lines.length},this.getTextRange=function(a){if(a.start.row==a.end.row)return this.$lines[a.start.row].substring(a.start.column,a.end.column);var b=[];b.push(this.$lines[a.start.row].substring(a.start.column)),b.push.apply(b,this.getLines(a.start.row+1,a.end.row-1)),b.push(this.$lines[a.end.row].substring(0,a.end.column));return b.join(this.getNewLineCharacter())},this.$clipPosition=function(a){var b=this.getLength();a.row>=b&&(a.row=Math.max(0,b-1),a.column=this.getLine(b-1).length);return a},this.insert=function(a,b){if(b.length==0)return a;a=this.$clipPosition(a),this.getLength()<=1&&this.$detectNewLine(b);var c=this.$split(b),d=c.splice(0,1)[0],e=c.length==0?null:c.splice(c.length-1,1)[0];a=this.insertInLine(a,d),e!==null&&(a=this.insertNewLine(a),a=this.insertLines(a.row,c),a=this.insertInLine(a,e||\"\"));return a},this.insertLines=function(a,b){if(b.length==0)return{row:a,column:0};var c=[a,0];c.push.apply(c,b),this.$lines.splice.apply(this.$lines,c);var d=new f(a,0,a+b.length,0),e={action:\"insertLines\",range:d,lines:b};this._dispatchEvent(\"change\",{data:e});return d.end},this.insertNewLine=function(a){a=this.$clipPosition(a);var b=this.$lines[a.row]||\"\";this.$lines[a.row]=b.substring(0,a.column),this.$lines.splice(a.row+1,0,b.substring(a.column,b.length));var c={row:a.row+1,column:0},d={action:\"insertText\",range:f.fromPoints(a,c),text:this.getNewLineCharacter()};this._dispatchEvent(\"change\",{data:d});return c},this.insertInLine=function(a,b){if(b.length==0)return a;var c=this.$lines[a.row]||\"\";this.$lines[a.row]=c.substring(0,a.column)+b+c.substring(a.column);var d={row:a.row,column:a.column+b.length},e={action:\"insertText\",range:f.fromPoints(a,d),text:b};this._dispatchEvent(\"change\",{data:e});return d},this.remove=function(a){a.start=this.$clipPosition(a.start),a.end=this.$clipPosition(a.end);if(a.isEmpty())return a.start;var b=a.start.row,c=a.end.row;if(a.isMultiLine()){var d=a.start.column==0?b:b+1,e=c-1;a.end.column>0&&this.removeInLine(c,0,a.end.column),e>=d&&this.removeLines(d,e),d!=b&&(this.removeInLine(b,a.start.column,this.getLine(b).length),this.removeNewLine(a.start.row))}else this.removeInLine(b,a.start.column,a.end.column);return a.start},this.removeInLine=function(a,b,c){if(b!=c){var d=new f(a,b,a,c),e=this.getLine(a),g=e.substring(b,c),h=e.substring(0,b)+e.substring(c,e.length);this.$lines.splice(a,1,h);var i={action:\"removeText\",range:d,text:g};this._dispatchEvent(\"change\",{data:i});return d.start}},this.removeLines=function(a,b){var c=new f(a,0,b+1,0),d=this.$lines.splice(a,b-a+1),e={action:\"removeLines\",range:c,nl:this.getNewLineCharacter(),lines:d};this._dispatchEvent(\"change\",{data:e});return d},this.removeNewLine=function(a){var b=this.getLine(a),c=this.getLine(a+1),d=new f(a,b.length,a+1,0),e=b+c;this.$lines.splice(a,2,e);var g={action:\"removeText\",range:d,text:this.getNewLineCharacter()};this._dispatchEvent(\"change\",{data:g})},this.replace=function(a,b){if(b.length==0&&a.isEmpty())return a.start;if(b==this.getTextRange(a))return a.end;this.remove(a);if(b)var c=this.insert(a.start,b);else c=a.start;return c},this.applyDeltas=function(a){for(var b=0;b<a.length;b++){var c=a[b],d=f.fromPoints(c.range.start,c.range.end);c.action==\"insertLines\"?this.insertLines(d.start.row,c.lines):c.action==\"insertText\"?this.insert(d.start,c.text):c.action==\"removeLines\"?this.removeLines(d.start.row,d.end.row-1):c.action==\"removeText\"&&this.remove(d)}},this.revertDeltas=function(a){for(var b=a.length-1;b>=0;b--){var c=a[b],d=f.fromPoints(c.range.start,c.range.end);c.action==\"insertLines\"?this.removeLines(d.start.row,d.end.row-1):c.action==\"insertText\"?this.remove(d):c.action==\"removeLines\"?this.insertLines(d.start.row,c.lines):c.action==\"removeText\"&&this.insert(d.start,c.text)}}}).call(h.prototype),b.Document=h}),define(\"ace/anchor\",function(a,b,c){var d=a(\"pilot/oop\"),e=a(\"pilot/event_emitter\").EventEmitter,f=b.Anchor=function(a,b,c){this.document=a,typeof c==\"undefined\"?this.setPosition(b.row,b.column):this.setPosition(b,c),this.$onChange=this.onChange.bind(this),a.on(\"change\",this.$onChange)};(function(){d.implement(this,e),this.getPosition=function(){return this.$clipPositionToDocument(this.row,this.column)},this.getDocument=function(){return this.document},this.onChange=function(a){var b=a.data,c=b.range;if(c.start.row!=c.end.row||c.start.row==this.row){if(c.start.row>this.row)return;if(c.start.row==this.row&&c.start.column>this.column)return;var d=this.row,e=this.column;b.action===\"insertText\"?c.start.row===d&&c.start.column<=e?c.start.row===c.end.row?e+=c.end.column-c.start.column:(e-=c.start.column,d+=c.end.row-c.start.row):c.start.row!==c.end.row&&c.start.row<d&&(d+=c.end.row-c.start.row):b.action===\"insertLines\"?c.start.row<=d&&(d+=c.end.row-c.start.row):b.action==\"removeText\"?c.start.row==d&&c.start.column<e?c.end.column>=e?e=c.start.column:e=Math.max(0,e-(c.end.column-c.start.column)):c.start.row!==c.end.row&&c.start.row<d?(c.end.row==d&&(e=Math.max(0,e-c.end.column)+c.start.column),d-=c.end.row-c.start.row):c.end.row==d&&(d-=c.end.row-c.start.row,e=Math.max(0,e-c.end.column)+c.start.column):b.action==\"removeLines\"&&c.start.row<=d&&(c.end.row<=d?d-=c.end.row-c.start.row:(d=c.start.row,e=0)),this.setPosition(d,e,!0)}},this.setPosition=function(a,b,c){c?pos={row:a,column:b}:pos=this.$clipPositionToDocument(a,b);if(this.row!=pos.row||this.column!=pos.column){var d={row:this.row,column:this.column};this.row=pos.row,this.column=pos.column,this._dispatchEvent(\"change\",{old:d,value:pos})}},this.detach=function(){this.document.removeEventListener(\"change\",this.$onChange)},this.$clipPositionToDocument=function(a,b){var c={};a>=this.document.getLength()?(c.row=Math.max(0,this.document.getLength()-1),c.column=this.document.getLine(c.row).length):a<0?(c.row=0,c.column=0):(c.row=a,c.column=Math.min(this.document.getLine(c.row).length,Math.max(0,b))),b<0&&(c.column=0);return c}}).call(f.prototype)}),define(\"ace/search\",function(a,b,c){var d=a(\"pilot/lang\"),e=a(\"pilot/oop\"),f=a(\"ace/range\").Range,g=function(){this.$options={needle:\"\",backwards:!1,wrap:!1,caseSensitive:!1,wholeWord:!1,scope:g.ALL,regExp:!1}};g.ALL=1,g.SELECTION=2,function(){this.set=function(a){e.mixin(this.$options,a);return this},this.getOptions=function(){return d.copyObject(this.$options)},this.find=function(a){if(!this.$options.needle)return null;if(this.$options.backwards)var b=this.$backwardMatchIterator(a);else b=this.$forwardMatchIterator(a);var c=null;b.forEach(function(a){c=a;return!0});return c},this.findAll=function(a){if(!this.$options.needle)return[];if(this.$options.backwards)var b=this.$backwardMatchIterator(a);else b=this.$forwardMatchIterator(a);var c=[];b.forEach(function(a){c.push(a)});return c},this.replace=function(a,b){var c=this.$assembleRegExp(),d=c.exec(a);return d&&d[0].length==a.length?this.$options.regExp?a.replace(c,b):b:null},this.$forwardMatchIterator=function(a){var b=this.$assembleRegExp(),c=this;return{forEach:function(d){c.$forwardLineIterator(a).forEach(function(a,e,f){e&&(a=a.substring(e));var g=[];a.replace(b,function(a){var b=arguments[arguments.length-2];g.push({str:a,offset:e+b});return a});for(var h=0;h<g.length;h++){var i=g[h],j=c.$rangeFromMatch(f,i.offset,i.str.length);if(d(j))return!0}})}}},this.$backwardMatchIterator=function(a){var b=this.$assembleRegExp(),c=this;return{forEach:function(d){c.$backwardLineIterator(a).forEach(function(a,e,f){e&&(a=a.substring(e));var g=[];a.replace(b,function(a,b){g.push({str:a,offset:e+b});return a});for(var h=g.length-1;h>=0;h--){var i=g[h],j=c.$rangeFromMatch(f,i.offset,i.str.length);if(d(j))return!0}})}}},this.$rangeFromMatch=function(a,b,c){return new f(a,b,a,b+c)},this.$assembleRegExp=function(){if(this.$options.regExp)var a=this.$options.needle;else a=d.escapeRegExp(this.$options.needle);this.$options.wholeWord&&(a=\"\\\\b\"+a+\"\\\\b\");var b=\"g\";this.$options.caseSensitive||(b+=\"i\");var c=new RegExp(a,b);return c},this.$forwardLineIterator=function(a){function k(e){var f=a.getLine(e);b&&e==c.end.row&&(f=f.substring(0,c.end.column)),j&&e==d.row&&(f=f.substring(0,d.column));return f}var b=this.$options.scope==g.SELECTION,c=a.getSelection().getRange(),d=a.getSelection().getCursor(),e=b?c.start.row:0,f=b?c.start.column:0,h=b?c.end.row:a.getLength()-1,i=this.$options.wrap,j=!1;return{forEach:function(a){var b=d.row,c=k(b),g=d.column,l=!1;j=!1;while(!a(c,g,b)){if(l)return;b++,g=0;if(b>h)if(i)b=e,g=f,j=!0;else return;b==d.row&&(l=!0),c=k(b)}}}},this.$backwardLineIterator=function(a){var b=this.$options.scope==g.SELECTION,c=a.getSelection().getRange(),d=b?c.end:c.start,e=b?c.start.row:0,f=b?c.start.column:0,h=b?c.end.row:a.getLength()-1,i=this.$options.wrap;return{forEach:function(g){var j=d.row,k=a.getLine(j).substring(0,d.column),l=0,m=!1,n=!1;while(!g(k,l,j)){if(m)return;j--,l=0;if(j<e)if(i)j=h,n=!0;else return;j==d.row&&(m=!0),k=a.getLine(j),b&&(j==e?l=f:j==h&&(k=k.substring(0,c.end.column))),n&&j==d.row&&(l=d.column)}}}}}.call(g.prototype),b.Search=g}),define(\"ace/background_tokenizer\",function(a,b,c){var d=a(\"pilot/oop\"),e=a(\"pilot/event_emitter\").EventEmitter,f=function(a,b){this.running=!1,this.lines=[],this.currentLine=0,this.tokenizer=a;var c=this;this.$worker=function(){if(!!c.running){var a=new Date,d=c.currentLine,e=c.doc,f=0,g=b.getLastVisibleRow(),h=e.getLength();while(c.currentLine<h){c.lines[c.currentLine]=c.$tokenizeRows(c.currentLine,c.currentLine)[0],c.currentLine++,f+=1;if(f%5==0&&new Date-a>20){c.fireUpdateEvent(d,c.currentLine-1);var i=c.currentLine<g?20:100;c.running=setTimeout(c.$worker,i);return}}c.running=!1,c.fireUpdateEvent(d,h-1)}}};(function(){d.implement(this,e),this.setTokenizer=function(a){this.tokenizer=a,this.lines=[],this.start(0)},this.setDocument=function(a){this.doc=a,this.lines=[],this.stop()},this.fireUpdateEvent=function(a,b){var c={first:a,last:b};this._dispatchEvent(\"update\",{data:c})},this.start=function(a){this.currentLine=Math.min(a||0,this.currentLine,this.doc.getLength()),this.lines.splice(this.currentLine,this.lines.length),this.stop(),this.running=setTimeout(this.$worker,700)},this.stop=function(){this.running&&clearTimeout(this.running),this.running=!1},this.getTokens=function(a,b){return this.$tokenizeRows(a,b)},this.getState=function(a){return this.$tokenizeRows(a,a)[0].state},this.$tokenizeRows=function(a,b){if(!this.doc)return[];var c=[],d=\"start\",e=!1;a>0&&this.lines[a-1]&&(d=this.lines[a-1].state,e=!0);var f=this.doc.getLines(a,b);for(var g=a;g<=b;g++)if(!this.lines[g]){var h=this.tokenizer.getLineTokens(f[g-a]||\"\",d),d=h.state;c.push(h),e&&(this.lines[g]=h)}else{var h=this.lines[g];d=h.state,c.push(h)}return c}}).call(f.prototype),b.BackgroundTokenizer=f}),define(\"ace/undomanager\",function(a,b,c){var d=function(){this.reset()};(function(){this.execute=function(a){var b=a.args[0];this.$doc=a.args[1],this.$undoStack.push(b)},this.undo=function(){var a=this.$undoStack.pop();a&&(this.$doc.undoChanges(a),this.$redoStack.push(a))},this.redo=function(){var a=this.$redoStack.pop();a&&(this.$doc.redoChanges(a),this.$undoStack.push(a))},this.reset=function(){this.$undoStack=[],this.$redoStack=[]},this.hasUndo=function(){return this.$undoStack.length>0},this.hasRedo=function(){return this.$redoStack.length>0}}).call(d.prototype),b.UndoManager=d}),define(\"ace/theme/textmate\",function(a,b,c){var d=a(\"pilot/dom\"),e=\".ace-tm .ace_editor {\\n  border: 2px solid rgb(159, 159, 159);\\n}\\n\\n.ace-tm .ace_editor.ace_focus {\\n  border: 2px solid #327fbd;\\n}\\n\\n.ace-tm .ace_gutter {\\n  width: 50px;\\n  background: #e8e8e8;\\n  color: #333;\\n  overflow : hidden;\\n}\\n\\n.ace-tm .ace_gutter-layer {\\n  width: 100%;\\n  text-align: right;\\n}\\n\\n.ace-tm .ace_gutter-layer .ace_gutter-cell {\\n  padding-right: 6px;\\n}\\n\\n.ace-tm .ace_print_margin {\\n  width: 1px;\\n  background: #e8e8e8;\\n}\\n\\n.ace-tm .ace_text-layer {\\n  cursor: text;\\n}\\n\\n.ace-tm .ace_cursor {\\n  border-left: 2px solid black;\\n}\\n\\n.ace-tm .ace_cursor.ace_overwrite {\\n  border-left: 0px;\\n  border-bottom: 1px solid black;\\n}\\n        \\n.ace-tm .ace_line .ace_invisible {\\n  color: rgb(191, 191, 191);\\n}\\n\\n.ace-tm .ace_line .ace_keyword {\\n  color: blue;\\n}\\n\\n.ace-tm .ace_line .ace_constant.ace_buildin {\\n  color: rgb(88, 72, 246);\\n}\\n\\n.ace-tm .ace_line .ace_constant.ace_language {\\n  color: rgb(88, 92, 246);\\n}\\n\\n.ace-tm .ace_line .ace_constant.ace_library {\\n  color: rgb(6, 150, 14);\\n}\\n\\n.ace-tm .ace_line .ace_invalid {\\n  background-color: rgb(153, 0, 0);\\n  color: white;\\n}\\n\\n.ace-tm .ace_line .ace_support.ace_function {\\n  color: rgb(60, 76, 114);\\n}\\n\\n.ace-tm .ace_line .ace_support.ace_constant {\\n  color: rgb(6, 150, 14);\\n}\\n\\n.ace-tm .ace_line .ace_support.ace_type,\\n.ace-tm .ace_line .ace_support.ace_class {\\n  color: rgb(109, 121, 222);\\n}\\n\\n.ace-tm .ace_line .ace_keyword.ace_operator {\\n  color: rgb(104, 118, 135);\\n}\\n\\n.ace-tm .ace_line .ace_string {\\n  color: rgb(3, 106, 7);\\n}\\n\\n.ace-tm .ace_line .ace_comment {\\n  color: rgb(76, 136, 107);\\n}\\n\\n.ace-tm .ace_line .ace_comment.ace_doc {\\n  color: rgb(0, 102, 255);\\n}\\n\\n.ace-tm .ace_line .ace_comment.ace_doc.ace_tag {\\n  color: rgb(128, 159, 191);\\n}\\n\\n.ace-tm .ace_line .ace_constant.ace_numeric {\\n  color: rgb(0, 0, 205);\\n}\\n\\n.ace-tm .ace_line .ace_variable {\\n  color: rgb(49, 132, 149);\\n}\\n\\n.ace-tm .ace_line .ace_xml_pe {\\n  color: rgb(104, 104, 91);\\n}\\n\\n.ace-tm .ace_marker-layer .ace_selection {\\n  background: rgb(181, 213, 255);\\n}\\n\\n.ace-tm .ace_marker-layer .ace_step {\\n  background: rgb(252, 255, 0);\\n}\\n\\n.ace-tm .ace_marker-layer .ace_stack {\\n  background: rgb(164, 229, 101);\\n}\\n\\n.ace-tm .ace_marker-layer .ace_bracket {\\n  margin: -1px 0 0 -1px;\\n  border: 1px solid rgb(192, 192, 192);\\n}\\n\\n.ace-tm .ace_marker-layer .ace_active_line {\\n  background: rgb(232, 242, 254);\\n}\\n\\n.ace-tm .ace_marker-layer .ace_selected_word {\\n  background: rgb(250, 250, 255);\\n  border: 1px solid rgb(200, 200, 250);\\n}\\n\\n.ace-tm .ace_string.ace_regex {\\n  color: rgb(255, 0, 0)\\n}\";d.importCssString(e),b.cssClass=\"ace-tm\"}),define(\"ace/mode/matching_brace_outdent\",function(a,b,c){var d=a(\"ace/range\").Range,e=function(){};(function(){this.checkOutdent=function(a,b){if(!/^\\s+$/.test(a))return!1;return/^\\s*\\}/.test(b)},this.autoOutdent=function(a,b){var c=a.getLine(b),e=c.match(/^(\\s*\\})/);if(!e)return 0;var f=e[1].length,g=a.findMatchingBracket({row:b,column:f});if(!g||g.row==b)return 0;var h=this.$getIndent(a.getLine(g.row));a.replace(new d(b,0,b,f-1),h)},this.$getIndent=function(a){var b=a.match(/^(\\s+)/);if(b)return b[1];return\"\"}}).call(e.prototype),b.MatchingBraceOutdent=e}),define(\"ace/virtual_renderer\",function(a,b,c){var d=a(\"pilot/oop\"),e=a(\"pilot/dom\"),f=a(\"pilot/event\"),g=a(\"pilot/useragent\"),h=a(\"ace/layer/gutter\").Gutter,i=a(\"ace/layer/marker\").Marker,j=a(\"ace/layer/text\").Text,k=a(\"ace/layer/cursor\").Cursor,l=a(\"ace/scrollbar\").ScrollBar,m=a(\"ace/renderloop\").RenderLoop,n=a(\"pilot/event_emitter\").EventEmitter,o=a(\"text/ace/css/editor.css\");e.importCssString(o);var p=function(a,b){this.container=a,e.addCssClass(this.container,\"ace_editor\"),this.setTheme(b),this.$gutter=e.createElement(\"div\"),this.$gutter.className=\"ace_gutter\",this.container.appendChild(this.$gutter),this.scroller=e.createElement(\"div\"),this.scroller.className=\"ace_scroller\",this.container.appendChild(this.scroller),this.content=e.createElement(\"div\"),this.content.className=\"ace_content\",this.scroller.appendChild(this.content),this.$gutterLayer=new h(this.$gutter),this.$markerBack=new i(this.content);var c=this.$textLayer=new j(this.content);this.canvas=c.element,this.$markerFront=new i(this.content),this.characterWidth=c.getCharacterWidth(),this.lineHeight=c.getLineHeight(),this.$cursorLayer=new k(this.content),this.$cursorPadding=8,this.$horizScroll=!0,this.$horizScrollAlwaysVisible=!0,this.scrollBar=new l(a),this.scrollBar.addEventListener(\"scroll\",this.onScroll.bind(this)),this.scrollTop=0,this.cursorPos={row:0,column:0};var d=this;this.$textLayer.addEventListener(\"changeCharaterSize\",function(){d.characterWidth=c.getCharacterWidth(),d.lineHeight=c.getLineHeight(),d.$updatePrintMargin(),d.onResize(!0),d.$loop.schedule(d.CHANGE_FULL)}),f.addListener(this.$gutter,\"click\",this.$onGutterClick.bind(this)),f.addListener(this.$gutter,\"dblclick\",this.$onGutterClick.bind(this)),this.$size={width:0,height:0,scrollerHeight:0,scrollerWidth:0},this.$loop=new m(this.$renderChanges.bind(this)),this.$loop.schedule(this.CHANGE_FULL),this.setPadding(4),this.$updatePrintMargin()};(function(){this.showGutter=!0,this.CHANGE_CURSOR=1,this.CHANGE_MARKER=2,this.CHANGE_GUTTER=4,this.CHANGE_SCROLL=8,this.CHANGE_LINES=16,this.CHANGE_TEXT=32,this.CHANGE_SIZE=64,this.CHANGE_MARKER_BACK=128,this.CHANGE_MARKER_FRONT=256,this.CHANGE_FULL=512,d.implement(this,n),this.setSession=function(a){this.session=a,this.$cursorLayer.setSession(a),this.$markerBack.setSession(a),this.$markerFront.setSession(a),this.$gutterLayer.setSession(a),this.$textLayer.setSession(a),this.$loop.schedule(this.CHANGE_FULL)},this.updateLines=function(a,b){b===undefined&&(b=Infinity),this.$changedLines?(this.$changedLines.firstRow>a&&(this.$changedLines.firstRow=a),this.$changedLines.lastRow<b&&(this.$changedLines.lastRow=b)):this.$changedLines={firstRow:a,lastRow:b},this.$loop.schedule(this.CHANGE_LINES)},this.updateText=function(){this.$loop.schedule(this.CHANGE_TEXT)},this.updateFull=function(){this.$loop.schedule(this.CHANGE_FULL)},this.updateFontSize=function(){this.$textLayer.checkForSizeChanges()},this.onResize=function(a){var b=this.CHANGE_SIZE,c=e.getInnerHeight(this.container);if(a||this.$size.height!=c)this.$size.height=c,this.scroller.style.height=c+\"px\",this.scrollBar.setHeight(this.scroller.clientHeight),this.session&&(this.scrollToY(this.getScrollTop()),b=b|this.CHANGE_FULL);var d=e.getInnerWidth(this.container);if(a||this.$size.width!=d){this.$size.width=d;var f=this.showGutter?this.$gutter.offsetWidth:0;this.scroller.style.left=f+\"px\",this.scroller.style.width=Math.max(0,d-f-this.scrollBar.getWidth())+\"px\";if(this.session.getUseWrapMode()){var g=this.scroller.clientWidth-this.$padding*2,h=Math.floor(g/this.characterWidth)-1;if(this.session.adjustWrapLimit(h)||a)b=b|this.CHANGE_FULL}}this.$size.scrollerWidth=this.scroller.clientWidth,this.$size.scrollerHeight=this.scroller.clientHeight,this.$loop.schedule(b)},this.setTokenizer=function(a){this.$tokenizer=a,this.$textLayer.setTokenizer(a),this.$loop.schedule(this.CHANGE_TEXT)},this.$onGutterClick=function(a){var b=f.getDocumentX(a),c=f.getDocumentY(a);this._dispatchEvent(\"gutter\"+a.type,{row:this.screenToTextCoordinates(b,c).row,htmlEvent:a})},this.setShowInvisibles=function(a){this.$textLayer.setShowInvisibles(a)&&this.$loop.schedule(this.CHANGE_TEXT)},this.getShowInvisibles=function(){return this.$textLayer.showInvisibles},this.$showPrintMargin=!0,this.setShowPrintMargin=function(a){this.$showPrintMargin=a,this.$updatePrintMargin()},this.getShowPrintMargin=function(){return this.$showPrintMargin},this.$printMarginColumn=80,this.setPrintMarginColumn=function(a){this.$printMarginColumn=a,this.$updatePrintMargin()},this.getPrintMarginColumn=function(){return this.$printMarginColumn},this.getShowGutter=function(){return this.showGutter},this.setShowGutter=function(a){this.showGutter!==a&&(this.$gutter.style.display=a?\"block\":\"none\",this.showGutter=a,this.onResize(!0))},this.$updatePrintMargin=function(){var a;if(!!this.$showPrintMargin||!!this.$printMarginEl){this.$printMarginEl||(a=e.createElement(\"div\"),a.className=\"ace_print_margin_layer\",this.$printMarginEl=e.createElement(\"div\"),this.$printMarginEl.className=\"ace_print_margin\",a.appendChild(this.$printMarginEl),this.content.insertBefore(a,this.$textLayer.element));var b=this.$printMarginEl.style;b.left=this.characterWidth*this.$printMarginColumn+this.$padding*2+\"px\",b.visibility=this.$showPrintMargin?\"visible\":\"hidden\"}},this.getContainerElement=function(){return this.container},this.getMouseEventTarget=function(){return this.content},this.getTextAreaContainer=function(){return this.container},this.moveTextAreaToCursor=function(a){if(!g.isIE){var b=this.$cursorLayer.getPixelPosition();if(!b)return;var c=this.content.getBoundingClientRect(),d=this.layerConfig&&this.layerConfig.offset||0;a.style.left=c.left+b.left+this.$padding+\"px\",a.style.top=c.top+b.top-this.scrollTop+d+\"px\"}},this.getFirstVisibleRow=function(){return(this.layerConfig||{}).firstRow||0},this.getFirstFullyVisibleRow=function(){if(!this.layerConfig)return 0;return this.layerConfig.firstRow+(this.layerConfig.offset==0?0:1)},this.getLastFullyVisibleRow=function(){if(!this.layerConfig)return 0;var a=Math.floor((this.layerConfig.height+this.layerConfig.offset)/this.layerConfig.lineHeight);return this.layerConfig.firstRow-1+a},this.getLastVisibleRow=function(){return(this.layerConfig||{}).lastRow||0},this.$padding=null,this.setPadding=function(a){this.$padding=a,this.content.style.padding=\"0 \"+a+\"px\",this.$loop.schedule(this.CHANGE_FULL),this.$updatePrintMargin()},this.getHScrollBarAlwaysVisible=function(){return this.$horizScrollAlwaysVisible},this.setHScrollBarAlwaysVisible=function(a){this.$horizScrollAlwaysVisible!=a&&(this.$horizScrollAlwaysVisible=a,(!this.$horizScrollAlwaysVisible||!this.$horizScroll)&&this.$loop.schedule(this.CHANGE_SCROLL))},this.onScroll=function(a){this.scrollToY(a.data)},this.$updateScrollBar=function(){this.scrollBar.setInnerHeight(this.session.getScreenLength()*this.lineHeight),this.scrollBar.setScrollTop(this.scrollTop)},this.$renderChanges=function(a){if(!(!a||!this.session||!this.$tokenizer)){(!this.layerConfig||a&this.CHANGE_FULL||a&this.CHANGE_SIZE||a&this.CHANGE_TEXT||a&this.CHANGE_LINES||a&this.CHANGE_SCROLL)&&this.$computeLayerConfig();if(a&this.CHANGE_FULL){this.$textLayer.update(this.layerConfig),this.showGutter&&this.$gutterLayer.update(this.layerConfig),this.$markerBack.update(this.layerConfig),this.$markerFront.update(this.layerConfig),this.$cursorLayer.update(this.layerConfig),this.$updateScrollBar(),this.scrollCursorIntoView();return}if(a&this.CHANGE_SCROLL){a&this.CHANGE_TEXT||a&this.CHANGE_LINES?this.$textLayer.update(this.layerConfig):this.$textLayer.scrollLines(this.layerConfig),this.showGutter&&this.$gutterLayer.update(this.layerConfig),this.$markerBack.update(this.layerConfig),this.$markerFront.update(this.layerConfig),this.$cursorLayer.update(this.layerConfig),this.$updateScrollBar();return}a&this.CHANGE_TEXT?(this.$textLayer.update(this.layerConfig),this.showGutter&&this.$gutterLayer.update(this.layerConfig)):a&this.CHANGE_LINES?(this.$updateLines(),this.$updateScrollBar(),this.showGutter&&this.$gutterLayer.update(this.layerConfig)):a&this.CHANGE_GUTTER&&this.showGutter&&this.$gutterLayer.update(this.layerConfig),a&this.CHANGE_CURSOR&&this.$cursorLayer.update(this.layerConfig),a&(this.CHANGE_MARKER|this.CHANGE_MARKER_FRONT)&&this.$markerFront.update(this.layerConfig),a&(this.CHANGE_MARKER|this.CHANGE_MARKER_BACK)&&this.$markerBack.update(this.layerConfig),a&this.CHANGE_SIZE&&this.$updateScrollBar()}},this.$computeLayerConfig=function(){var a=this.session,b=this.scrollTop%this.lineHeight,c=this.$size.scrollerHeight+this.lineHeight,d=this.$getLongestLine(),e=this.layerConfig?this.layerConfig.width!=d:!0,f=this.$horizScrollAlwaysVisible||this.$size.scrollerWidth-d<0,g=this.$horizScroll!==f;this.$horizScroll=f,g&&(this.scroller.style.overflowX=f?\"scroll\":\"hidden\");var h=Math.ceil(c/this.lineHeight)-1,i=Math.max(0,Math.round((this.scrollTop-b)/this.lineHeight)),j=i+h,k,l,m={lineHeight:this.lineHeight};i=a.screenToDocumentRow(i),k=a.documentToScreenRow(i),l=a.getRowHeight(m,i),j=Math.min(a.screenToDocumentRow(j),a.getLength()-1),c=this.$size.scrollerHeight+a.getRowHeight(m,j)+l,b=this.scrollTop-k*this.lineHeight;var n=this.layerConfig={width:d,padding:this.$padding,firstRow:i,firstRowScreen:k,lastRow:j,lineHeight:this.lineHeight,characterWidth:this.characterWidth,minHeight:c,offset:b,height:this.$size.scrollerHeight};this.$gutterLayer.element.style.marginTop=-b+\"px\",this.content.style.marginTop=-b+\"px\",this.content.style.width=d+\"px\",this.content.style.height=c+\"px\",g&&this.onResize(!0)},this.$updateLines=function(){var a=this.$changedLines.firstRow,b=this.$changedLines.lastRow;this.$changedLines=null;var c=this.layerConfig;if(c.width!=this.$getLongestLine())return this.$textLayer.update(c);if(!(a>c.lastRow+1)){if(b<c.firstRow)return;if(b===Infinity){this.showGutter&&this.$gutterLayer.update(c),this.$textLayer.update(c);return}this.$textLayer.updateLines(c,a,b)}},this.$getLongestLine=function(){var a=this.session.getScreenWidth()+1;this.$textLayer.showInvisibles&&(a+=1);return Math.max(this.$size.scrollerWidth-this.$padding*2,Math.round(a*this.characterWidth))},this.updateFrontMarkers=function(){this.$markerFront.setMarkers(this.session.getMarkers(!0)),this.$loop.schedule(this.CHANGE_MARKER_FRONT)},this.updateBackMarkers=function(){this.$markerBack.setMarkers(this.session.getMarkers()),this.$loop.schedule(this.CHANGE_MARKER_BACK)},this.addGutterDecoration=function(a,b){this.$gutterLayer.addGutterDecoration(a,b),this.$loop.schedule(this.CHANGE_GUTTER)},this.removeGutterDecoration=function(a,b){this.$gutterLayer.removeGutterDecoration(a,b),this.$loop.schedule(this.CHANGE_GUTTER)},this.setBreakpoints=function(a){this.$gutterLayer.setBreakpoints(a),this.$loop.schedule(this.CHANGE_GUTTER)},this.setAnnotations=function(a){this.$gutterLayer.setAnnotations(a),this.$loop.schedule(this.CHANGE_GUTTER)},this.updateCursor=function(){this.$loop.schedule(this.CHANGE_CURSOR)},this.hideCursor=function(){this.$cursorLayer.hideCursor()},this.showCursor=function(){this.$cursorLayer.showCursor()},this.scrollCursorIntoView=function(){if(this.$size.scrollerHeight!==0){var a=this.$cursorLayer.getPixelPosition(),b=a.left+this.$padding,c=a.top;this.getScrollTop()>c&&this.scrollToY(c),this.getScrollTop()+this.$size.scrollerHeight<c+this.lineHeight&&this.scrollToY(c+this.lineHeight-this.$size.scrollerHeight),this.scroller.scrollLeft>b&&this.scrollToX(b),this.scroller.scrollLeft+this.$size.scrollerWidth<b+this.characterWidth&&(b+this.characterWidth>this.scroller.scrollWidth&&this.$renderChanges(this.CHANGE_SIZE),this.scrollToX(Math.round(b+this.characterWidth-this.$size.scrollerWidth)))}},this.getScrollTop=function(){return this.scrollTop},this.getScrollLeft=function(){return this.scroller.scrollLeft},this.getScrollTopRow=function(){return this.scrollTop/this.lineHeight},this.getScrollBottomRow=function(){return Math.max(0,Math.floor((this.scrollTop+this.$size.scrollerHeight)/this.lineHeight)-1)},this.scrollToRow=function(a){this.scrollToY(a*this.lineHeight)},this.scrollToLine=function(a,b){var c={lineHeight:this.lineHeight},d=0;for(var e=1;e<a;e++)d+=this.session.getRowHeight(c,e-1);b&&(d-=this.$size.scrollerHeight/2),this.scrollToY(d)},this.scrollToY=function(a){var b=this.session.getScreenLength()*this.lineHeight-this.$size.scrollerHeight,a=Math.max(0,Math.min(b,a));this.scrollTop!==a&&(this.scrollTop=a,this.$loop.schedule(this.CHANGE_SCROLL))},this.scrollToX=function(a){a<=this.$padding&&(a=0),this.scroller.scrollLeft=a},this.scrollBy=function(a,b){b&&this.scrollToY(this.scrollTop+b),a&&this.scrollToX(this.scroller.scrollLeft+a)},this.screenToTextCoordinates=function(a,b){var c=this.scroller.getBoundingClientRect(),d=Math.round((a+this.scroller.scrollLeft-c.left-this.$padding-e.getPageScrollLeft())/this.characterWidth),f=Math.floor((b+this.scrollTop-c.top-e.getPageScrollTop())/this.lineHeight);return this.session.screenToDocumentPosition(f,Math.max(d,0))},this.textToScreenCoordinates=function(a,b){var c=this.scroller.getBoundingClientRect(),d=this.session.documentToScreenPosition(a,b),e=this.$padding+Math.round(d.column*this.characterWidth),f=d.row*this.lineHeight;return{pageX:c.left+e-this.getScrollLeft(),pageY:c.top+f-this.getScrollTop()}},this.visualizeFocus=function(){e.addCssClass(this.container,\"ace_focus\")},this.visualizeBlur=function(){e.removeCssClass(this.container,\"ace_focus\")},this.showComposition=function(a){this.$composition||(this.$composition=e.createElement(\"div\"),this.$composition.className=\"ace_composition\",this.content.appendChild(this.$composition)),this.$composition.innerHTML=\"&#160;\";var b=this.$cursorLayer.getPixelPosition(),c=this.$composition.style;c.top=b.top+\"px\",c.left=b.left+this.$padding+\"px\",c.height=this.lineHeight+\"px\",this.hideCursor()},this.setCompositionText=function(a){e.setInnerText(this.$composition,a)},this.hideComposition=function(){this.showCursor();if(!!this.$composition){var a=this.$composition.style;a.top=\"-10000px\",a.left=\"-10000px\"}},this.setTheme=function(b){function d(a){c.$theme&&e.removeCssClass(c.container,c.$theme),c.$theme=a?a.cssClass:null,c.$theme&&e.addCssClass(c.container,c.$theme),c.$size&&(c.$size.width=0,c.onResize())}var c=this;!b||typeof b==\"string\"?(b=b||\"ace/theme/textmate\",a([b],function(a){d(a)})):d(b);var c=this},this.setStyle=function b(a){e.addCssClass(this.container,a)},this.unsetStyle=function c(a){e.removeCssClass(this.container,a)}}).call(p.prototype),b.VirtualRenderer=p}),define(\"ace/layer/gutter\",function(a,b,c){var d=a(\"pilot/dom\"),e=function(a){this.element=d.createElement(\"div\"),this.element.className=\"ace_layer ace_gutter-layer\",a.appendChild(this.element),this.$breakpoints=[],this.$annotations=[],this.$decorations=[]};(function(){this.setSession=function(a){this.session=a},this.addGutterDecoration=function(a,b){this.$decorations[a]||(this.$decorations[a]=\"\"),this.$decorations[a]+=\" ace_\"+b},this.removeGutterDecoration=function(a,b){this.$decorations[a]=this.$decorations[a].replace(\" ace_\"+b,\"\")},this.setBreakpoints=function(a){this.$breakpoints=a.concat()},this.setAnnotations=function(a){this.$annotations=[];for(var b in a)if(a.hasOwnProperty(b)){var c=a[b];if(!c)continue;var d=this.$annotations[b]={text:[]};for(var e=0;e<c.length;e++){var f=c[e];d.text.push(f.text.replace(/\"/g,\"&quot;\").replace(/'/g,\"&#8217;\").replace(/</,\"&lt;\"));var g=f.type;g==\"error\"?d.className=\"ace_error\":g==\"warning\"&&d.className!=\"ace_error\"?d.className=\"ace_warning\":g==\"info\"&&!d.className&&(d.className=\"ace_info\")}}},this.update=function(a){this.$config=a;var b=[];for(var c=a.firstRow;c<=a.lastRow;c++){var e=this.$annotations[c]||{className:\"\",text:[]};b.push(\"<div class='ace_gutter-cell\",this.$decorations[c]||\"\",this.$breakpoints[c]?\" ace_breakpoint \":\" \",e.className,\"' title='\",e.text.join(\"\\n\"),\"' style='height:\",this.session.getRowHeight(a,c),\"px;'>\",c+1,\"</div>\")}this.element=d.setInnerHtml(this.element,b.join(\"\")),this.element.style.height=a.minHeight+\"px\"}}).call(e.prototype),b.Gutter=e}),define(\"ace/layer/marker\",function(a,b,c){var d=a(\"ace/range\").Range,e=a(\"pilot/dom\"),f=function(a){this.element=e.createElement(\"div\"),this.element.className=\"ace_layer ace_marker-layer\",a.appendChild(this.element)};(function(){this.setSession=function(a){this.session=a},this.setMarkers=function(a){this.markers=a},this.update=function(a){var a=a||this.config;if(!!a){this.config=a;var b=[];for(var c in this.markers){var d=this.markers[c],f=d.range.clipRows(a.firstRow,a.lastRow);if(f.isEmpty())continue;f=f.toScreenRange(this.session);if(d.renderer){var g=this.$getTop(f.start.row,a),h=Math.round(f.start.column*a.characterWidth);d.renderer(b,f,h,g,a)}else f.isMultiLine()?d.type==\"text\"?this.drawTextMarker(b,f,d.clazz,a):this.drawMultiLineMarker(b,f,d.clazz,a):this.drawSingleLineMarker(b,f,d.clazz,a)}this.element=e.setInnerHtml(this.element,b.join(\"\"))}},this.$getTop=function(a,b){return(a-b.firstRowScreen)*b.lineHeight},this.drawTextMarker=function(a,b,c,e){var f=b.start.row,g=new d(f,b.start.column,f,this.session.getScreenLastRowColumn(f));this.drawSingleLineMarker(a,g,c,e,1);var f=b.end.row,g=new d(f,0,f,b.end.column);this.drawSingleLineMarker(a,g,c,e);for(var f=b.start.row+1;f<b.end.row;f++)g.start.row=f,g.end.row=f,g.end.column=this.session.getScreenLastRowColumn(f),this.drawSingleLineMarker(a,g,c,e,1)},this.drawMultiLineMarker=function(a,b,c,d){var e=d.lineHeight,f=Math.round(d.width-b.start.column*d.characterWidth),g=this.$getTop(b.start.row,d),h=Math.round(b.start.column*d.characterWidth);a.push(\"<div class='\",c,\"' style='\",\"height:\",e,\"px;\",\"width:\",f,\"px;\",\"top:\",g,\"px;\",\"left:\",h,\"px;'></div>\");var g=this.$getTop(b.end.row,d),f=Math.round(b.end.column*d.characterWidth);a.push(\"<div class='\",c,\"' style='\",\"height:\",e,\"px;\",\"top:\",g,\"px;\",\"width:\",f,\"px;'></div>\");var e=(b.end.row-b.start.row-1)*d.lineHeight;if(!(e<0)){var g=this.$getTop(b.start.row+1,d);a.push(\"<div class='\",c,\"' style='\",\"height:\",e,\"px;\",\"width:\",d.width,\"px;\",\"top:\",g,\"px;'></div>\")}},this.drawSingleLineMarker=function(a,b,c,d,e){var f=d.lineHeight,g=Math.round((b.end.column+(e||0)-b.start.column)*d.characterWidth),h=this.$getTop(b.start.row,d),i=Math.round(b.start.column*d.characterWidth);a.push(\"<div class='\",c,\"' style='\",\"height:\",f,\"px;\",\"width:\",g,\"px;\",\"top:\",h,\"px;\",\"left:\",i,\"px;'></div>\")}}).call(f.prototype),b.Marker=f}),define(\"ace/layer/text\",function(a,b,c){var d=a(\"pilot/oop\"),e=a(\"pilot/dom\"),f=a(\"pilot/lang\"),g=a(\"pilot/event_emitter\").EventEmitter,h=function(a){this.element=e.createElement(\"div\"),this.element.className=\"ace_layer ace_text-layer\",a.appendChild(this.element),this.$characterSize=this.$measureSizes()||{width:0,height:0},this.$pollSizeChanges()};(function(){d.implement(this,g),this.EOF_CHAR=\"&para;\",this.EOL_CHAR=\"&not;\",this.TAB_CHAR=\"&rarr;\",this.SPACE_CHAR=\"&middot;\",this.setTokenizer=function(a){this.tokenizer=a},this.getLineHeight=function(){return this.$characterSize.height||1},this.getCharacterWidth=function(){return this.$characterSize.width||1},this.checkForSizeChanges=function(){var a=this.$measureSizes();a&&(this.$characterSize.width!==a.width||this.$characterSize.height!==a.height)&&(this.$characterSize=a,this._dispatchEvent(\"changeCharaterSize\",{data:a}))},this.$pollSizeChanges=function(){var a=this;setInterval(function(){a.checkForSizeChanges()},500)},this.$fontStyles={fontFamily:1,fontSize:1,fontWeight:1,fontStyle:1,lineHeight:1},this.$measureSizes=function(){var a=1e3;if(!this.$measureNode){var b=this.$measureNode=e.createElement(\"div\"),c=b.style;c.width=c.height=\"auto\",c.left=c.top=-a*40+\"px\",c.visibility=\"hidden\",c.position=\"absolute\",c.overflow=\"visible\",c.whiteSpace=\"nowrap\",b.innerHTML=f.stringRepeat(\"Xy\",a);if(document.body)document.body.appendChild(b);else{var d=this.element.parentNode;while(!e.hasCssClass(d,\"ace_editor\"))d=d.parentNode;d.appendChild(b)}}var c=this.$measureNode.style;for(var g in this.$fontStyles){var h=e.computedStyle(this.element,g);c[g]=h}var i={height:this.$measureNode.offsetHeight,width:this.$measureNode.offsetWidth/(a*2)};if(i.width==0&&i.height==0)return null;return i},this.setSession=function(a){this.session=a},this.showInvisibles=!1,this.setShowInvisibles=function(a){if(this.showInvisibles==a)return!1;this.showInvisibles=a;return!0},this.$computeTabString=function(){var a=this.session.getTabSize();if(this.showInvisibles){var b=a/2;this.$tabString=\"<span class='ace_invisible'>\"+Array(Math.floor(b)).join(\"&#160;\")+this.TAB_CHAR+Array(Math.ceil(b)+1).join(\"&#160;\")+\"</span>\"}else this.$tabString=Array(a+1).join(\"&#160;\")},this.updateLines=function(a,b,c){this.$computeTabString(),(this.config.lastRow!=a.lastRow||this.config.firstRow!=a.firstRow)&&this.scrollLines(a),this.config=a;var d=Math.max(b,a.firstRow),f=Math.min(c,a.lastRow),g=this.element.childNodes,h=this.tokenizer.getTokens(d,f);for(var i=d;i<=f;i++){var j=g[i-a.firstRow];if(!j)continue;var k=[];this.$renderLine(k,i,h[i-d].tokens),j=e.setInnerHtml(j,k.join(\"\")),j.style.height=this.session.getRowHeight(a,i)+\"px\"}},this.scrollLines=function(a){this.$computeTabString();var b=this.config;this.config=a;if(!b||b.lastRow<a.firstRow)return this.update(a);if(a.lastRow<b.firstRow)return this.update(a);var c=this.element;if(b.firstRow<a.firstRow)for(var d=b.firstRow;d<a.firstRow;d++)c.removeChild(c.firstChild);if(b.lastRow>a.lastRow)for(var d=a.lastRow+1;d<=b.lastRow;d++)c.removeChild(c.lastChild);if(a.firstRow<b.firstRow){var e=this.$renderLinesFragment(a,a.firstRow,b.firstRow-1);c.firstChild?c.insertBefore(e,c.firstChild):c.appendChild(e)}if(a.lastRow>b.lastRow){var e=this.$renderLinesFragment(a,b.lastRow+1,a.lastRow);c.appendChild(e)}},this.$renderLinesFragment=function(a,b,c){var d=document.createDocumentFragment(),f=this.tokenizer.getTokens(b,c);for(var g=b;g<=c;g++){var h=e.createElement(\"div\");h.className=\"ace_line\";var i=h.style;i.height=this.session.getRowHeight(a,g)+\"px\",i.width=a.width+\"px\";var j=[];f.length>g-b&&this.$renderLine(j,g,f[g-b].tokens),h.innerHTML=j.join(\"\"),d.appendChild(h)}return d},this.update=function(a){this.$computeTabString(),this.config=a;var b=[],c=this.tokenizer.getTokens(a.firstRow,a.lastRow),d=this.$renderLinesFragment(a,a.firstRow,a.lastRow);this.element.innerHTML=\"\",this.element.appendChild(d)},this.$textToken={text:!0,rparen:!0,lparen:!0},this.$renderLine=function(a,b,c){function i(b,c){var d=c.replace(/&/g,\"&amp;\").replace(/</g,\"&lt;\").replace(e,f).replace(/\\t/g,g.$tabString).replace(/[\\u3040-\\u309F]|[\\u30A0-\\u30FF]|[\\u4E00-\\u9FFF\\uF900-\\uFAFF\\u3400-\\u4DBF]/g,function(a){return\"<span class='ace_cjk' style='width:\"+h*2+\"px'>\"+a+\"</span>\"});if(!g.$textToken[b.type]){var i=\"ace_\"+b.type.replace(/\\./g,\" ace_\");a.push(\"<span class='\",i,\"'>\",d,\"</span>\")}else a.push(d)}if(this.showInvisibles)var d=this,e=/( +)|([\\v\\f \\u00a0\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u200b\\u2028\\u2029\\u3000])/g,f=function(a){if(a.charCodeAt(0)==32)return Array(a.length+1).join(\"&#160;\");var a=Array(a.length+1).join(d.SPACE_CHAR);return\"<span class='ace_invisible'>\"+a+\"</span>\"};else var e=/[\\v\\f \\u00a0\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u200b\\u2028\\u2029\\u3000]/g,f=\"&#160;\";var g=this,h=this.config.characterWidth,j=this.session.getRowSplitData(b),k=0,l=0,m;!j||j.length==0?m=Number.MAX_VALUE:m=j[0],a.push(\"<div style='height:\",this.config.lineHeight,\"px\",\"'>\");for(var n=0;n<c.length;n++){var o=c[n],p=o.value;if(k+p.length<m)i(o,p),k+=p.length;else{while(k+p.length>=m)i(o,p.substring(0,m-k)),p=p.substring(m-k),k=m,a.push(\"</div>\",\"<div style='height:\",this.config.lineHeight,\"px\",\"'>\"),l++,m=j[l]||Number.MAX_VALUE;p.length!=0&&(k+=p.length,i(o,p))}}this.showInvisibles&&(b!==this.session.getLength()-1?a.push(\"<span class='ace_invisible'>\"+this.EOL_CHAR+\"</span>\"):a.push(\"<span class='ace_invisible'>\"+this.EOF_CHAR+\"</span>\")),a.push(\"</div>\")}}).call(h.prototype),b.Text=h}),define(\"ace/layer/cursor\",function(a,b,c){var d=a(\"pilot/dom\"),e=function(a){this.element=d.createElement(\"div\"),this.element.className=\"ace_layer ace_cursor-layer\",a.appendChild(this.element),this.cursor=d.createElement(\"div\"),this.cursor.className=\"ace_cursor\",this.isVisible=!1};(function(){this.setSession=function(a){this.session=a},this.hideCursor=function(){this.isVisible=!1,this.cursor.parentNode&&this.cursor.parentNode.removeChild(this.cursor),clearInterval(this.blinkId)},this.showCursor=function(){this.isVisible=!0,this.element.appendChild(this.cursor);var a=this.cursor;a.style.visibility=\"visible\",this.restartTimer()},this.restartTimer=function(){clearInterval(this.blinkId);if(!!this.isVisible){var a=this.cursor;this.blinkId=setInterval(function(){a.style.visibility=\"hidden\",setTimeout(function(){a.style.visibility=\"visible\"},400)},1e3)}},this.getPixelPosition=function(a){if(!this.config||!this.session)return{left:0,top:0};var b=this.session.selection.getCursor(),c=this.session.documentToScreenPosition(b),d=Math.round(c.column*this.config.characterWidth),e=(c.row-(a?this.config.firstRowScreen:0))*this.config.lineHeight;return{left:d,top:e}},this.update=function(a){this.config=a,this.pixelPos=this.getPixelPosition(!0),this.cursor.style.left=this.pixelPos.left+\"px\",this.cursor.style.top=this.pixelPos.top+\"px\",this.cursor.style.width=a.characterWidth+\"px\",this.cursor.style.height=a.lineHeight+\"px\",this.isVisible&&this.element.appendChild(this.cursor),this.session.getOverwrite()?d.addCssClass(this.cursor,\"ace_overwrite\"):d.removeCssClass(this.cursor,\"ace_overwrite\"),this.restartTimer()}}).call(e.prototype),b.Cursor=e}),define(\"ace/scrollbar\",function(a,b,c){var d=a(\"pilot/oop\"),e=a(\"pilot/dom\"),f=a(\"pilot/event\"),g=a(\"pilot/event_emitter\").EventEmitter,h=function(a){this.element=e.createElement(\"div\"),this.element.className=\"ace_sb\",this.inner=e.createElement(\"div\"),this.element.appendChild(this.inner),a.appendChild(this.element),this.width=e.scrollbarWidth(),this.element.style.width=this.width+\"px\",f.addListener(this.element,\"scroll\",this.onScroll.bind(this))};(function(){d.implement(this,g),this.onScroll=function(){this._dispatchEvent(\"scroll\",{data:this.element.scrollTop})},this.getWidth=function(){return this.width},this.setHeight=function(a){this.element.style.height=a+\"px\"},this.setInnerHeight=function(a){this.inner.style.height=a+\"px\"},this.setScrollTop=function(a){this.element.scrollTop=a}}).call(h.prototype),b.ScrollBar=h}),define(\"ace/renderloop\",function(a,b,c){var d=a(\"pilot/event\"),e=function(a){this.onRender=a,this.pending=!1,this.changes=0};(function(){this.schedule=function(a){this.changes=this.changes|a;if(!this.pending){this.pending=!0;var b=this;this.setTimeoutZero(function(){b.pending=!1;var a=b.changes;b.changes=0,b.onRender(a)})}},window.postMessage?(this.messageName=\"zero-timeout-message\",this.setTimeoutZero=function(a){if(!this.attached){var b=this;d.addListener(window,\"message\",function(a){b.callback&&a.data==b.messageName&&(d.stopPropagation(a),b.callback())}),this.attached=!0}this.callback=a,window.postMessage(this.messageName,\"*\")}):this.setTimeoutZero=function(a){setTimeout(a,0)}}).call(e.prototype),b.RenderLoop=e}),define(\"text/ace/css/editor.css\",'.ace_editor {    position: absolute;    overflow: hidden;    font-family: \"Menlo\", \"Monaco\", \"Courier New\", monospace;    font-size: 12px;  }.ace_scroller {    position: absolute;    overflow-x: scroll;    overflow-y: hidden;     }.ace_content {    position: absolute;    box-sizing: border-box;    -moz-box-sizing: border-box;    -webkit-box-sizing: border-box;}.ace_composition {    position: absolute;    background: #555;    color: #DDD;    z-index: 4;}.ace_gutter {    position: absolute;    overflow-x: hidden;    overflow-y: hidden;    height: 100%;}.ace_gutter-cell.ace_error {    background-image: url(\"data:image/gif,GIF89a%10%00%10%00%D5%00%00%F5or%F5%87%88%F5nr%F4ns%EBmq%F5z%7F%DDJT%DEKS%DFOW%F1Yc%F2ah%CE(7%CE)8%D18E%DD%40M%F2KZ%EBU%60%F4%60m%DCir%C8%16(%C8%19*%CE%255%F1%3FR%F1%3FS%E6%AB%B5%CA%5DI%CEn%5E%F7%A2%9A%C9G%3E%E0a%5B%F7%89%85%F5yy%F6%82%80%ED%82%80%FF%BF%BF%E3%C4%C4%FF%FF%FF%FF%FF%FF%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00!%F9%04%01%00%00%25%00%2C%00%00%00%00%10%00%10%00%00%06p%C0%92pH%2C%1A%8F%C8%D2H%93%E1d4%23%E4%88%D3%09mB%1DN%B48%F5%90%40%60%92G%5B%94%20%3E%22%D2%87%24%FA%20%24%C5%06A%00%20%B1%07%02B%A38%89X.v%17%82%11%13q%10%0Fi%24%0F%8B%10%7BD%12%0Ei%09%92%09%0EpD%18%15%24%0A%9Ci%05%0C%18F%18%0B%07%04%01%04%06%A0H%18%12%0D%14%0D%12%A1I%B3%B4%B5IA%00%3B\");    background-repeat: no-repeat;    background-position: 4px center;}.ace_gutter-cell.ace_warning {    background-image: url(\"data:image/gif,GIF89a%10%00%10%00%D5%00%00%FF%DBr%FF%DE%81%FF%E2%8D%FF%E2%8F%FF%E4%96%FF%E3%97%FF%E5%9D%FF%E6%9E%FF%EE%C1%FF%C8Z%FF%CDk%FF%D0s%FF%D4%81%FF%D5%82%FF%D5%83%FF%DC%97%FF%DE%9D%FF%E7%B8%FF%CCl%7BQ%13%80U%15%82W%16%81U%16%89%5B%18%87%5B%18%8C%5E%1A%94d%1D%C5%83-%C9%87%2F%C6%84.%C6%85.%CD%8B2%C9%871%CB%8A3%CD%8B5%DC%98%3F%DF%9BB%E0%9CC%E1%A5U%CB%871%CF%8B5%D1%8D6%DB%97%40%DF%9AB%DD%99B%E3%B0p%E7%CC%AE%FF%FF%FF%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00!%F9%04%01%00%00%2F%00%2C%00%00%00%00%10%00%10%00%00%06a%C0%97pH%2C%1A%8FH%A1%ABTr%25%87%2B%04%82%F4%7C%B9X%91%08%CB%99%1C!%26%13%84*iJ9(%15G%CA%84%14%01%1A%97%0C%03%80%3A%9A%3E%81%84%3E%11%08%B1%8B%20%02%12%0F%18%1A%0F%0A%03\\'F%1C%04%0B%10%16%18%10%0B%05%1CF%1D-%06%07%9A%9A-%1EG%1B%A0%A1%A0U%A4%A5%A6BA%00%3B\");    background-repeat: no-repeat;    background-position: 4px center;}.ace_editor .ace_sb {    position: absolute;    overflow-x: hidden;    overflow-y: scroll;    right: 0;}.ace_editor .ace_sb div {    position: absolute;    width: 1px;    left: 0;}.ace_editor .ace_print_margin_layer {    z-index: 0;    position: absolute;    overflow: hidden;    margin: 0;    left: 0;    height: 100%;    width: 100%;}.ace_editor .ace_print_margin {    position: absolute;    height: 100%;}.ace_editor textarea {    position: fixed;    z-index: -1;    width: 10px;    height: 30px;    opacity: 0;    background: transparent;    appearance: none;    border: none;    resize: none;    outline: none;    overflow: hidden;}.ace_layer {    z-index: 1;    position: absolute;    overflow: hidden;      white-space: nowrap;    height: 100%;    width: 100%;}.ace_text-layer {    font-family: Monaco, \"Courier New\", monospace;    color: black;}.ace_cjk {    display: inline-block;    text-align: center;}.ace_cursor-layer {    z-index: 4;    cursor: text;    pointer-events: none;}.ace_cursor {    z-index: 4;    position: absolute;}.ace_line {    white-space: nowrap;}.ace_marker-layer {    cursor: text;}.ace_marker-layer .ace_step {    position: absolute;    z-index: 3;}.ace_marker-layer .ace_selection {    position: absolute;    z-index: 4;}.ace_marker-layer .ace_bracket {    position: absolute;    z-index: 5;}.ace_marker-layer .ace_active_line {    position: absolute;    z-index: 2;}.ace_marker-layer .ace_selected_word {    position: absolute;    z-index: 6;    box-sizing: border-box;    -moz-box-sizing: border-box;    -webkit-box-sizing: border-box;}.ace_dragging .ace_marker-layer, .ace_dragging .ace_text-layer {  cursor: move;}'),define(\"text/styles.css\",\"html {    height: 100%;    overflow: hidden;}body {    overflow: hidden;    margin: 0;    padding: 0;    height: 100%;    width: 100%;    font-family: Arial, Helvetica, sans-serif, Tahoma, Verdana, sans-serif;    font-size: 12px;    background: rgb(14, 98, 165);    color: white;}#editor {    position: absolute;    top: 60px;    left: 0px;    background: white;}#controls {    width: 100%;}#cockpitInput {    position: absolute;    width: 100%;    bottom: 0;    border: none; outline: none;    font-family: consolas, courier, monospace;    font-size: 120%;}#cockpitOutput {    padding: 10px;    margin: 0 15px;    border: 1px solid #AAA;    -moz-border-radius-topleft: 10px;    -moz-border-radius-topright: 10px;    border-top-left-radius: 4px; border-top-right-radius: 4px;    background: #DDD; color: #000;}\");var deps=[\"pilot/fixoldbrowsers\",\"pilot/index\",\"pilot/plugin_manager\",\"pilot/environment\",\"ace/editor\",\"ace/edit_session\",\"ace/virtual_renderer\",\"ace/undomanager\",\"ace/theme/textmate\"];require(deps,function(){var a=require(\"pilot/plugin_manager\").catalog;a.registerPlugins([\"pilot/index\"]);var b=require(\"pilot/dom\"),c=require(\"pilot/event\"),d=require(\"ace/editor\").Editor,e=require(\"ace/edit_session\").EditSession,f=require(\"ace/undomanager\").UndoManager,g=require(\"ace/virtual_renderer\").VirtualRenderer;window.ace={edit:function(h){typeof h==\"string\"&&(h=document.getElementById(h));var i=new e(b.getInnerText(h));i.setUndoManager(new f),h.innerHTML=\"\";var j=new d(new g(h,\"ace/theme/textmate\"));j.setSession(i);var k=require(\"pilot/environment\").create();a.startupPlugins({env:k}).then(function(){k.document=i,k.editor=j,j.resize(),c.addListener(window,\"resize\",function(){j.resize()}),h.env=k}),j.env=k;return j}}})"
  },
  {
    "path": "dirigible/shared/static/ace/cockpit-uncompressed.js",
    "content": "/* ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Mozilla Skywriter.\n *\n * The Initial Developer of the Original Code is\n * Mozilla.\n * Portions created by the Initial Developer are Copyright (C) 2009\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *      Kevin Dangoor (kdangoor@mozilla.com)\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('cockpit/index', function(require, exports, module) {\n\n\nexports.startup = function(data, reason) {\n  require('pilot/index');\n  require('cockpit/cli').startup(data, reason);\n  // window.testCli = require('cockpit/test/testCli');\n\n  require('cockpit/ui/settings').startup(data, reason);\n  require('cockpit/ui/cli_view').startup(data, reason);\n  require('cockpit/commands/basic').startup(data, reason);\n};\n\n/*\nexports.shutdown(data, reason) {\n};\n*/\n\n\n});\n/* ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Skywriter.\n *\n * The Initial Developer of the Original Code is\n * Mozilla.\n * Portions created by the Initial Developer are Copyright (C) 2009\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *   Joe Walker (jwalker@mozilla.com)\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('cockpit/cli', function(require, exports, module) {\n\n\nvar console = require('pilot/console');\nvar lang = require('pilot/lang');\nvar oop = require('pilot/oop');\nvar EventEmitter = require('pilot/event_emitter').EventEmitter;\n\n//var keyboard = require('keyboard/keyboard');\nvar types = require('pilot/types');\nvar Status = require('pilot/types').Status;\nvar Conversion = require('pilot/types').Conversion;\nvar canon = require('pilot/canon');\n\n/**\n * Normally type upgrade is done when the owning command is registered, but\n * out commandParam isn't part of a command, so it misses out.\n */\nexports.startup = function(data, reason) {\n    canon.upgradeType('command', commandParam);\n};\n\n/**\n * The information required to tell the user there is a problem with their\n * input.\n * TODO: There a several places where {start,end} crop up. Perhaps we should\n * have a Cursor object.\n */\nfunction Hint(status, message, start, end, predictions) {\n    this.status = status;\n    this.message = message;\n\n    if (typeof start === 'number') {\n        this.start = start;\n        this.end = end;\n        this.predictions = predictions;\n    }\n    else {\n        var arg = start;\n        this.start = arg.start;\n        this.end = arg.end;\n        this.predictions = arg.predictions;\n    }\n}\nHint.prototype = {\n};\n/**\n * Loop over the array of hints finding the one we should display.\n * @param hints array of hints\n */\nHint.sort = function(hints, cursor) {\n    // Calculate 'distance from cursor'\n    if (cursor !== undefined) {\n        hints.forEach(function(hint) {\n            if (hint.start === Argument.AT_CURSOR) {\n                hint.distance = 0;\n            }\n            else if (cursor < hint.start) {\n                hint.distance = hint.start - cursor;\n            }\n            else if (cursor > hint.end) {\n                hint.distance = cursor - hint.end;\n            }\n            else {\n                hint.distance = 0;\n            }\n        }, this);\n    }\n    // Sort\n    hints.sort(function(hint1, hint2) {\n        // Compare first based on distance from cursor\n        if (cursor !== undefined) {\n            var diff = hint1.distance - hint2.distance;\n            if (diff != 0) {\n                return diff;\n            }\n        }\n        // otherwise go with hint severity\n        return hint2.status - hint1.status;\n    });\n    // tidy-up\n    if (cursor !== undefined) {\n        hints.forEach(function(hint) {\n            delete hint.distance;\n        }, this);\n    }\n    return hints;\n};\nexports.Hint = Hint;\n\n/**\n * A Hint that arose as a result of a Conversion\n */\nfunction ConversionHint(conversion, arg) {\n    this.status = conversion.status;\n    this.message = conversion.message;\n    if (arg) {\n        this.start = arg.start;\n        this.end = arg.end;\n    }\n    else {\n        this.start = 0;\n        this.end = 0;\n    }\n    this.predictions = conversion.predictions;\n};\noop.inherits(ConversionHint, Hint);\n\n\n/**\n * We record where in the input string an argument comes so we can report errors\n * against those string positions.\n * We publish a 'change' event when-ever the text changes\n * @param emitter Arguments use something else to pass on change events.\n * Currently this will be the creating Requisition. This prevents dependency\n * loops and prevents us from needing to merge listener lists.\n * @param text The string (trimmed) that contains the argument\n * @param start The position of the text in the original input string\n * @param end See start\n * @param prefix Knowledge of quotation marks and whitespace used prior to the\n * text in the input string allows us to re-generate the original input from\n * the arguments.\n * @param suffix Any quotation marks and whitespace used after the text.\n * Whitespace is normally placed in the prefix to the succeeding argument, but\n * can be used here when this is the last argument.\n * @constructor\n */\nfunction Argument(emitter, text, start, end, prefix, suffix) {\n    this.emitter = emitter;\n    this.setText(text);\n    this.start = start;\n    this.end = end;\n    this.prefix = prefix;\n    this.suffix = suffix;\n}\nArgument.prototype = {\n    /**\n     * Return the result of merging these arguments.\n     * TODO: What happens when we're merging arguments for the single string\n     * case and some of the arguments are in quotation marks?\n     */\n    merge: function(following) {\n        if (following.emitter != this.emitter) {\n            throw new Error('Can\\'t merge Arguments from different EventEmitters');\n        }\n        return new Argument(\n            this.emitter,\n            this.text + this.suffix + following.prefix + following.text,\n            this.start, following.end,\n            this.prefix,\n            following.suffix);\n    },\n\n    /**\n     * See notes on events in Assignment. We might need to hook changes here\n     * into a CliRequisition so they appear of the command line.\n     */\n    setText: function(text) {\n        if (text == null) {\n            throw new Error('Illegal text for Argument: ' + text);\n        }\n        var ev = { argument: this, oldText: this.text, text: text };\n        this.text = text;\n        this.emitter._dispatchEvent('argumentChange', ev);\n    },\n\n    /**\n     * Helper when we're putting arguments back together\n     */\n    toString: function() {\n        // TODO: There is a bug here - we should re-escape escaped characters\n        // But can we do that reliably?\n        return this.prefix + this.text + this.suffix;\n    }\n};\n\n/**\n * Merge an array of arguments into a single argument.\n * All Arguments in the array are expected to have the same emitter\n */\nArgument.merge = function(argArray, start, end) {\n    start = (start === undefined) ? 0 : start;\n    end = (end === undefined) ? argArray.length : end;\n\n    var joined;\n    for (var i = start; i < end; i++) {\n        var arg = argArray[i];\n        if (!joined) {\n            joined = arg;\n        }\n        else {\n            joined = joined.merge(arg);\n        }\n    }\n    return joined;\n};\n\n/**\n * We sometimes need a way to say 'this error occurs where ever the cursor is'\n */\nArgument.AT_CURSOR = -1;\n\n\n/**\n * A link between a parameter and the data for that parameter.\n * The data for the parameter is available as in the preferred type and as\n * an Argument for the CLI.\n * <p>We also record validity information where applicable.\n * <p>For values, null and undefined have distinct definitions. null means\n * that a value has been provided, undefined means that it has not.\n * Thus, null is a valid default value, and common because it identifies an\n * parameter that is optional. undefined means there is no value from\n * the command line.\n * @constructor\n */\nfunction Assignment(param, requisition) {\n    this.param = param;\n    this.requisition = requisition;\n    this.setValue(param.defaultValue);\n};\nAssignment.prototype = {\n    /**\n     * The parameter that we are assigning to\n     * @readonly\n     */\n    param: undefined,\n\n    /**\n     * Report on the status of the last parse() conversion.\n     * @see types.Conversion\n     */\n    conversion: undefined,\n\n    /**\n     * The current value in a type as specified by param.type\n     */\n    value: undefined,\n\n    /**\n     * The string version of the current value\n     */\n    arg: undefined,\n\n    /**\n     * The current value (i.e. not the string representation)\n     * Use setValue() to mutate\n     */\n    value: undefined,\n    setValue: function(value) {\n        if (this.value === value) {\n            return;\n        }\n\n        if (value === undefined) {\n            this.value = this.param.defaultValue;\n            this.conversion = this.param.getDefault ?\n                    this.param.getDefault() :\n                    this.param.type.getDefault();\n            this.arg = undefined;\n        } else {\n            this.value = value;\n            this.conversion = undefined;\n            var text = (value == null) ? '' : this.param.type.stringify(value);\n            if (this.arg) {\n                this.arg.setText(text);\n            }\n        }\n\n        this.requisition._assignmentChanged(this);\n    },\n\n    /**\n     * The textual representation of the current value\n     * Use setValue() to mutate\n     */\n    arg: undefined,\n    setArgument: function(arg) {\n        if (this.arg === arg) {\n            return;\n        }\n        this.arg = arg;\n        this.conversion = this.param.type.parse(arg.text);\n        this.conversion.arg = arg; // TODO: make this automatic?\n        this.value = this.conversion.value;\n        this.requisition._assignmentChanged(this);\n    },\n\n    /**\n     * Create a list of the hints associated with this parameter assignment.\n     * Generally there will be only one hint generated because we're currently\n     * only displaying one hint at a time, ordering by distance from cursor\n     * and severity. Since distance from cursor will be the same for all hints\n     * from this assignment all but the most severe will ever be used. It might\n     * make sense with more experience to alter this to function to be getHint()\n     */\n    getHint: function() {\n        // Allow the parameter to provide documentation\n        if (this.param.getCustomHint && this.value && this.arg) {\n            var hint = this.param.getCustomHint(this.value, this.arg);\n            if (hint) {\n                return hint;\n            }\n        }\n\n        // If there is no argument, use the cursor position\n        var message = '<strong>' + this.param.name + '</strong>: ';\n        if (this.param.description) {\n            // TODO: This should be a short description - do we need to trim?\n            message += this.param.description.trim();\n\n            // Ensure the help text ends with '. '\n            if (message.charAt(message.length - 1) !== '.') {\n                message += '.';\n            }\n            if (message.charAt(message.length - 1) !== ' ') {\n                message += ' ';\n            }\n        }\n        var status = Status.VALID;\n        var start = this.arg ? this.arg.start : Argument.AT_CURSOR;\n        var end = this.arg ? this.arg.end : Argument.AT_CURSOR;\n        var predictions;\n\n        // Non-valid conversions will have useful information to pass on\n        if (this.conversion) {\n            status = this.conversion.status;\n            if (this.conversion.message) {\n                message += this.conversion.message;\n            }\n            predictions = this.conversion.predictions;\n        }\n\n        // Hint if the param is required, but not provided\n        var argProvided = this.arg && this.arg.text !== '';\n        var dataProvided = this.value !== undefined || argProvided;\n        if (this.param.defaultValue === undefined && !dataProvided) {\n            status = Status.INVALID;\n            message += '<strong>Required<\\strong>';\n        }\n\n        return new Hint(status, message, start, end, predictions);\n    },\n\n    /**\n     * Basically <tt>setValue(conversion.predictions[0])</tt> done in a safe\n     * way.\n     */\n    complete: function() {\n        if (this.conversion && this.conversion.predictions &&\n                this.conversion.predictions.length > 0) {\n            this.setValue(this.conversion.predictions[0]);\n        }\n    },\n\n    /**\n     * If the cursor is at 'position', do we have sufficient data to start\n     * displaying the next hint. This is both complex and important.\n     * For example, if the user has just typed:<ul>\n     * <li>'set tabstop ' then they clearly want to know about the valid\n     *     values for the tabstop setting, so the hint is based on the next\n     *     parameter.\n     * <li>'set tabstop' (without trailing space) - they will probably still\n     *     want to know about the valid values for the tabstop setting because\n     *     there is no confusion about the setting in question.\n     * <li>'set tabsto' they've not finished typing a setting name so the hint\n     *     should be based on the current parameter.\n     * <li>'set tabstop' (when there is an additional tabstopstyle setting) we\n     *     can't make assumptions about the setting - we're not finished.\n     * </ul>\n     * <p>Note that the input for 2 and 4 is identical, only the configuration\n     * has changed, so hint display is environmental.\n     *\n     * <p>This function works out if the cursor is before the end of this\n     * assignment (assuming that we've asked the same thing of the previous\n     * assignment) and then attempts to work out if we should use the hint from\n     * the next assignment even though technically the cursor is still inside\n     * this one due to the rules above.\n     */\n    isPositionCaptured: function(position) {\n        if (!this.arg) {\n            return false;\n        }\n\n        // Note we don't check if position >= this.arg.start because that's\n        // implied by the fact that we're asking the assignments in turn, and\n        // we want to avoid thing falling between the cracks, but we do need\n        // to check that the argument does have a position\n        if (this.arg.start === -1) {\n            return false;\n        }\n\n        // We're clearly done if the position is past the end of the text\n        if (position > this.arg.end) {\n            return false;\n        }\n\n        // If we're AT the end, the position is captured if either the status\n        // is not valid or if there are other valid options including current\n        if (position === this.arg.end) {\n            return this.conversion.status !== Status.VALID ||\n                    this.conversion.predictions.length !== 0;\n        }\n\n        // Otherwise we're clearly inside\n        return true;\n    },\n\n    /**\n     * Replace the current value with the lower value if such a concept\n     * exists.\n     */\n    decrement: function() {\n        var replacement = this.param.type.decrement(this.value);\n        if (replacement != null) {\n            this.setValue(replacement);\n        }\n    },\n\n    /**\n     * Replace the current value with the higher value if such a concept\n     * exists.\n     */\n    increment: function() {\n        var replacement = this.param.type.increment(this.value);\n        if (replacement != null) {\n            this.setValue(replacement);\n        }\n    },\n\n    /**\n     * Helper when we're rebuilding command lines.\n     */\n    toString: function() {\n        return this.arg ? this.arg.toString() : '';\n    }\n};\nexports.Assignment = Assignment;\n\n\n/**\n * This is a special parameter to reflect the command itself.\n */\nvar commandParam = {\n    name: '__command',\n    type: 'command',\n    description: 'The command to execute',\n\n    /**\n     * Provide some documentation for a command.\n     */\n    getCustomHint: function(command, arg) {\n        var docs = [];\n        docs.push('<strong><tt> &gt; ');\n        docs.push(command.name);\n        if (command.params && command.params.length > 0) {\n            command.params.forEach(function(param) {\n                if (param.defaultValue === undefined) {\n                    docs.push(' [' + param.name + ']');\n                }\n                else {\n                    docs.push(' <em>[' + param.name + ']</em>');\n                }\n            }, this);\n        }\n        docs.push('</tt></strong><br/>');\n\n        docs.push(command.description ? command.description : '(No description)');\n        docs.push('<br/>');\n\n        if (command.params && command.params.length > 0) {\n            docs.push('<ul>');\n            command.params.forEach(function(param) {\n                docs.push('<li>');\n                docs.push('<strong><tt>' + param.name + '</tt></strong>: ');\n                docs.push(param.description ? param.description : '(No description)');\n                if (param.defaultValue === undefined) {\n                    docs.push(' <em>[Required]</em>');\n                }\n                else if (param.defaultValue === null) {\n                    docs.push(' <em>[Optional]</em>');\n                }\n                else {\n                    docs.push(' <em>[Default: ' + param.defaultValue + ']</em>');\n                }\n                docs.push('</li>');\n            }, this);\n            docs.push('</ul>');\n        }\n\n        return new Hint(Status.VALID, docs.join(''), arg);\n    }\n};\n\n/**\n * A Requisition collects the information needed to execute a command.\n * There is no point in a requisition for parameter-less commands because there\n * is no information to collect. A Requisition is a collection of assignments\n * of values to parameters, each handled by an instance of Assignment.\n * CliRequisition adds functions for parsing input from a command line to this\n * class.\n * <h2>Events<h2>\n * We publish the following events:<ul>\n * <li>argumentChange: The text of some argument has changed. It is likely that\n * any UI component displaying this argument will need to be updated. (Note that\n * this event is actually published by the Argument itself - see the docs for\n * Argument for more details)\n * The event object looks like: { argument: A, oldText: B, text: B }\n * <li>commandChange: The command has changed. It is likely that a UI\n * structure will need updating to match the parameters of the new command.\n * The event object looks like { command: A }\n * @constructor\n */\nfunction Requisition(env) {\n    this.env = env;\n    this.commandAssignment = new Assignment(commandParam, this);\n}\n\nRequisition.prototype = {\n    /**\n     * The command that we are about to execute.\n     * @see setCommandConversion()\n     * @readonly\n     */\n    commandAssignment: undefined,\n\n    /**\n     * The count of assignments. Excludes the commandAssignment\n     * @readonly\n     */\n    assignmentCount: undefined,\n\n    /**\n     * The object that stores of Assignment objects that we are filling out.\n     * The Assignment objects are stored under their param.name for named\n     * lookup. Note: We make use of the property of Javascript objects that\n     * they are not just hashmaps, but linked-list hashmaps which iterate in\n     * insertion order.\n     * Excludes the commandAssignment.\n     */\n    _assignments: undefined,\n\n    /**\n     * The store of hints generated by the assignments. We are trying to prevent\n     * the UI from needing to access this in broad form, but instead use\n     * methods that query part of this structure.\n     */\n    _hints: undefined,\n\n    /**\n     * When the command changes, we need to keep a bunch of stuff in sync\n     */\n    _assignmentChanged: function(assignment) {\n        // This is all about re-creating Assignments\n        if (assignment.param.name !== '__command') {\n            return;\n        }\n\n        this._assignments = {};\n\n        if (assignment.value) {\n            assignment.value.params.forEach(function(param) {\n                this._assignments[param.name] = new Assignment(param, this);\n            }, this);\n        }\n\n        this.assignmentCount = Object.keys(this._assignments).length;\n        this._dispatchEvent('commandChange', { command: assignment.value });\n    },\n\n    /**\n     * Assignments have an order, so we need to store them in an array.\n     * But we also need named access ...\n     */\n    getAssignment: function(nameOrNumber) {\n        var name = (typeof nameOrNumber === 'string') ?\n            nameOrNumber :\n            Object.keys(this._assignments)[nameOrNumber];\n        return this._assignments[name];\n    },\n\n    /**\n     * Where parameter name == assignment names - they are the same.\n     */\n    getParameterNames: function() {\n        return Object.keys(this._assignments);\n    },\n\n    /**\n     * A *shallow* clone of the assignments.\n     * This is useful for systems that wish to go over all the assignments\n     * finding values one way or another and wish to trim an array as they go.\n     */\n    cloneAssignments: function() {\n        return Object.keys(this._assignments).map(function(name) {\n            return this._assignments[name];\n        }, this);\n    },\n\n    /**\n     * Collect the statuses from the Assignments.\n     * The hints returned are sorted by severity\n     */\n    _updateHints: function() {\n        // TODO: work out when to clear this out for the plain Requisition case\n        // this._hints = [];\n        this.getAssignments(true).forEach(function(assignment) {\n            this._hints.push(assignment.getHint());\n        }, this);\n        Hint.sort(this._hints);\n\n        // We would like to put some initial help here, but for anyone but\n        // a complete novice a 'type help' message is very annoying, so we\n        // need to find a way to only display this message once, or for\n        // until the user click a 'close' button or similar\n        // TODO: Add special case for '' input\n    },\n\n    /**\n     * Returns the most severe status\n     */\n    getWorstHint: function() {\n        return this._hints[0];\n    },\n\n    /**\n     * Extract the names and values of all the assignments, and return as\n     * an object.\n     */\n    getArgsObject: function() {\n        var args = {};\n        this.getAssignments().forEach(function(assignment) {\n            args[assignment.param.name] = assignment.value;\n        }, this);\n        return args;\n    },\n\n    /**\n     * Access the arguments as an array.\n     * @param includeCommand By default only the parameter arguments are\n     * returned unless (includeCommand === true), in which case the list is\n     * prepended with commandAssignment.arg\n     */\n    getAssignments: function(includeCommand) {\n        var args = [];\n        if (includeCommand === true) {\n            args.push(this.commandAssignment);\n        }\n        Object.keys(this._assignments).forEach(function(name) {\n            args.push(this.getAssignment(name));\n        }, this);\n        return args;\n    },\n\n    /**\n     * Reset all the assignments to their default values\n     */\n    setDefaultValues: function() {\n        this.getAssignments().forEach(function(assignment) {\n            assignment.setValue(undefined);\n        }, this);\n    },\n\n    /**\n     * Helper to call canon.exec\n     */\n    exec: function() {\n        canon.exec(this.commandAssignment.value,\n              this.env,\n              \"cli\",\n              this.getArgsObject(),\n              this.toCanonicalString());\n    },\n\n    /**\n     * Extract a canonical version of the input\n     */\n    toCanonicalString: function() {\n        var line = [];\n        line.push(this.commandAssignment.value.name);\n        Object.keys(this._assignments).forEach(function(name) {\n            var assignment = this._assignments[name];\n            var type = assignment.param.type;\n            // TODO: This will cause problems if there is a non-default value\n            // after a default value. Also we need to decide when to use\n            // named parameters in place of positional params. Both can wait.\n            if (assignment.value !== assignment.param.defaultValue) {\n                line.push(' ');\n                line.push(type.stringify(assignment.value));\n            }\n        }, this);\n        return line.join('');\n    }\n};\noop.implement(Requisition.prototype, EventEmitter);\nexports.Requisition = Requisition;\n\n\n/**\n * An object used during command line parsing to hold the various intermediate\n * data steps.\n * <p>The 'output' of the update is held in 2 objects: input.hints which is an\n * array of hints to display to the user. In the future this will become a\n * single value.\n * <p>The other output value is input.requisition which gives access to an\n * args object for use in executing the final command.\n *\n * <p>The majority of the functions in this class are called in sequence by the\n * constructor. Their task is to add to <tt>hints</tt> fill out the requisition.\n * <p>The general sequence is:<ul>\n * <li>_tokenize(): convert _typed into _parts\n * <li>_split(): convert _parts into _command and _unparsedArgs\n * <li>_assign(): convert _unparsedArgs into requisition\n * </ul>\n *\n * @param typed {string} The instruction as typed by the user so far\n * @param options {object} A list of optional named parameters. Can be any of:\n * <b>flags</b>: Flags for us to check against the predicates specified with the\n * commands. Defaulted to <tt>keyboard.buildFlags({ });</tt>\n * if not specified.\n * @constructor\n */\nfunction CliRequisition(env, options) {\n    Requisition.call(this, env);\n\n    if (options && options.flags) {\n        /**\n         * TODO: We were using a default of keyboard.buildFlags({ });\n         * This allowed us to have commands that only existed in certain contexts\n         * - i.e. Javascript specific commands.\n         */\n        this.flags = options.flags;\n    }\n}\noop.inherits(CliRequisition, Requisition);\n(function() {\n    /**\n     * Called by the UI when ever the user interacts with a command line input\n     * @param input A structure that details the state of the input field.\n     * It should look something like: { typed:a, cursor: { start:b, end:c } }\n     * Where a is the contents of the input field, and b and c are the start\n     * and end of the cursor/selection respectively.\n     */\n    CliRequisition.prototype.update = function(input) {\n        this.input = input;\n        this._hints = [];\n\n        var args = this._tokenize(input.typed);\n        this._split(args);\n\n        if (this.commandAssignment.value) {\n            this._assign(args);\n        }\n\n        this._updateHints();\n    };\n\n    /**\n     * Return an array of Status scores so we can create a marked up\n     * version of the command line input.\n     */\n    CliRequisition.prototype.getInputStatusMarkup = function() {\n        // 'scores' is an array which tells us what chars are errors\n        // Initialize with everything VALID\n        var scores = this.toString().split('').map(function(ch) {\n            return Status.VALID;\n        });\n        // For all chars in all hints, check and upgrade the score\n        this._hints.forEach(function(hint) {\n            for (var i = hint.start; i <= hint.end; i++) {\n                if (hint.status > scores[i]) {\n                    scores[i] = hint.status;\n                }\n            }\n        }, this);\n        return scores;\n    };\n\n    /**\n     * Reconstitute the input from the args\n     */\n    CliRequisition.prototype.toString = function() {\n        return this.getAssignments(true).map(function(assignment) {\n            return assignment.toString();\n        }, this).join('');\n    };\n\n    var superUpdateHints = CliRequisition.prototype._updateHints;\n    /**\n     * Marks up hints in a number of ways:\n     * - Makes INCOMPLETE hints that are not near the cursor INVALID since\n     *   they can't be completed by typing\n     * - Finds the most severe hint, and annotates the array with it\n     * - Finds the hint to display, and also annotates the array with it\n     * TODO: I'm wondering if array annotation is evil and we should replace\n     * this with an object. Need to find out more.\n     */\n    CliRequisition.prototype._updateHints = function() {\n        superUpdateHints.call(this);\n\n        // Not knowing about cursor positioning, the requisition and assignments\n        // can't know this, but anything they mark as INCOMPLETE is actually\n        // INVALID unless the cursor is actually inside that argument.\n        var c = this.input.cursor;\n        this._hints.forEach(function(hint) {\n            var startInHint = c.start >= hint.start && c.start <= hint.end;\n            var endInHint = c.end >= hint.start && c.end <= hint.end;\n            var inHint = startInHint || endInHint;\n            if (!inHint && hint.status === Status.INCOMPLETE) {\n                 hint.status = Status.INVALID;\n            }\n        }, this);\n\n        Hint.sort(this._hints);\n    };\n\n    /**\n     * Accessor for the hints array.\n     * While we could just use the hints property, using getHints() is\n     * preferred for symmetry with Requisition where it needs a function due to\n     * lack of an atomic update system.\n     */\n    CliRequisition.prototype.getHints = function() {\n        return this._hints;\n    };\n\n    /**\n     * Look through the arguments attached to our assignments for the assignment\n     * at the given position.\n     */\n    CliRequisition.prototype.getAssignmentAt = function(position) {\n        var assignments = this.getAssignments(true);\n        for (var i = 0; i < assignments.length; i++) {\n            var assignment = assignments[i];\n            if (!assignment.arg) {\n                // There is no argument in this assignment, we've fallen off\n                // the end of the obvious answers - it must be this one.\n                return assignment;\n            }\n            if (assignment.isPositionCaptured(position)) {\n                return assignment;\n            }\n        }\n\n        return assignment;\n    };\n\n    /**\n     * Split up the input taking into account ' and \"\n     */\n    CliRequisition.prototype._tokenize = function(typed) {\n        // For blank input, place a dummy empty argument into the list\n        if (typed == null || typed.length === 0) {\n            return [ new Argument(this, '', 0, 0, '', '') ];\n        }\n\n        var OUTSIDE = 1;     // The last character was whitespace\n        var IN_SIMPLE = 2;   // The last character was part of a parameter\n        var IN_SINGLE_Q = 3; // We're inside a single quote: '\n        var IN_DOUBLE_Q = 4; // We're inside double quotes: \"\n\n        var mode = OUTSIDE;\n\n        // First we un-escape. This list was taken from:\n        // https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Core_Language_Features#Unicode\n        // We are generally converting to their real values except for \\', \\\"\n        // and '\\ ' which we are converting to unicode private characters so we\n        // can distinguish them from ', \" and ' ', which have special meaning.\n        // They need swapping back post-split - see unescape2()\n        typed = typed\n                .replace(/\\\\\\\\/g, '\\\\')\n                .replace(/\\\\b/g, '\\b')\n                .replace(/\\\\f/g, '\\f')\n                .replace(/\\\\n/g, '\\n')\n                .replace(/\\\\r/g, '\\r')\n                .replace(/\\\\t/g, '\\t')\n                .replace(/\\\\v/g, '\\v')\n                .replace(/\\\\n/g, '\\n')\n                .replace(/\\\\r/g, '\\r')\n                .replace(/\\\\ /g, '\\uF000')\n                .replace(/\\\\'/g, '\\uF001')\n                .replace(/\\\\\"/g, '\\uF002');\n\n        function unescape2(str) {\n            return str\n                .replace(/\\uF000/g, ' ')\n                .replace(/\\uF001/g, '\\'')\n                .replace(/\\uF002/g, '\"');\n        }\n\n        var i = 0;\n        var start = 0; // Where did this section start?\n        var prefix = '';\n        var args = [];\n\n        while (true) {\n            if (i >= typed.length) {\n                // There is nothing else to read - tidy up\n                if (mode !== OUTSIDE) {\n                    var str = unescape2(typed.substring(start, i));\n                    args.push(new Argument(this, str, start, i, prefix, ''));\n                }\n                else {\n                    if (i !== start) {\n                        // There's a bunch of whitespace at the end of the\n                        // command add it to the last argument's suffix,\n                        // creating an empty argument if needed.\n                        var extra = typed.substring(start, i);\n                        var lastArg = args[args.length - 1];\n                        if (!lastArg) {\n                            lastArg = new Argument(this, '', i, i, extra, '');\n                            args.push(lastArg);\n                        }\n                        else {\n                            lastArg.suffix += extra;\n                        }\n                    }\n                }\n                break;\n            }\n\n            var c = typed[i];\n            switch (mode) {\n                case OUTSIDE:\n                    if (c === '\\'') {\n                        prefix = typed.substring(start, i + 1);\n                        mode = IN_SINGLE_Q;\n                        start = i + 1;\n                    }\n                    else if (c === '\"') {\n                        prefix = typed.substring(start, i + 1);\n                        mode = IN_DOUBLE_Q;\n                        start = i + 1;\n                    }\n                    else if (/ /.test(c)) {\n                        // Still whitespace, do nothing\n                    }\n                    else {\n                        prefix = typed.substring(start, i);\n                        mode = IN_SIMPLE;\n                        start = i;\n                    }\n                    break;\n\n                case IN_SIMPLE:\n                    // There is an edge case of xx'xx which we are assuming to\n                    // be a single parameter (and same with \")\n                    if (c === ' ') {\n                        var str = unescape2(typed.substring(start, i));\n                        args.push(new Argument(this, str,\n                                start, i, prefix, ''));\n                        mode = OUTSIDE;\n                        start = i;\n                        prefix = '';\n                    }\n                    break;\n\n                case IN_SINGLE_Q:\n                    if (c === '\\'') {\n                        var str = unescape2(typed.substring(start, i));\n                        args.push(new Argument(this, str,\n                                start - 1, i + 1, prefix, c));\n                        mode = OUTSIDE;\n                        start = i + 1;\n                        prefix = '';\n                    }\n                    break;\n\n                case IN_DOUBLE_Q:\n                    if (c === '\"') {\n                        var str = unescape2(typed.substring(start, i));\n                        args.push(new Argument(this, str,\n                                start - 1, i + 1, prefix, c));\n                        mode = OUTSIDE;\n                        start = i + 1;\n                        prefix = '';\n                    }\n                    break;\n            }\n\n            i++;\n        }\n\n        return args;\n    };\n\n    /**\n     * Looks in the canon for a command extension that matches what has been\n     * typed at the command line.\n     */\n    CliRequisition.prototype._split = function(args) {\n        var argsUsed = 1;\n        var arg;\n\n        while (argsUsed <= args.length) {\n            var arg = Argument.merge(args, 0, argsUsed);\n            this.commandAssignment.setArgument(arg);\n\n            if (!this.commandAssignment.value) {\n                // Not found. break with value == null\n                break;\n            }\n\n            /*\n            // Previously we needed a way to hide commands depending context.\n            // We have not resurrected that feature yet.\n            if (!keyboard.flagsMatch(command.predicates, this.flags)) {\n                // If the predicates say 'no match' then go LA LA LA\n                command = null;\n                break;\n            }\n            */\n\n            if (this.commandAssignment.value.exec) {\n                // Valid command, break with command valid\n                for (var i = 0; i < argsUsed; i++) {\n                    args.shift();\n                }\n                break;\n            }\n\n            argsUsed++;\n        }\n    };\n\n    /**\n     * Work out which arguments are applicable to which parameters.\n     * <p>This takes #_command.params and #_unparsedArgs and creates a map of\n     * param names to 'assignment' objects, which have the following properties:\n     * <ul>\n     * <li>param - The matching parameter.\n     * <li>index - Zero based index into where the match came from on the input\n     * <li>value - The matching input\n     * </ul>\n     */\n    CliRequisition.prototype._assign = function(args) {\n        if (args.length === 0) {\n            this.setDefaultValues();\n            return;\n        }\n\n        // Create an error if the command does not take parameters, but we have\n        // been given them ...\n        if (this.assignmentCount === 0) {\n            // TODO: previously we were doing some extra work to avoid this if\n            // we determined that we had args that were all whitespace, but\n            // probably given our tighter tokenize() this won't be an issue?\n            this._hints.push(new Hint(Status.INVALID,\n                    this.commandAssignment.value.name +\n                    ' does not take any parameters',\n                    Argument.merge(args)));\n            return;\n        }\n\n        // Special case: if there is only 1 parameter, and that's of type\n        // text we put all the params into the first param\n        if (this.assignmentCount === 1) {\n            var assignment = this.getAssignment(0);\n            if (assignment.param.type.name === 'text') {\n                assignment.setArgument(Argument.merge(args));\n                return;\n            }\n        }\n\n        var assignments = this.cloneAssignments();\n        var names = this.getParameterNames();\n\n        // Extract all the named parameters\n        var used = [];\n        assignments.forEach(function(assignment) {\n            var namedArgText = '--' + assignment.name;\n\n            var i = 0;\n            while (true) {\n                var arg = args[i];\n                if (namedArgText !== arg.text) {\n                    i++;\n                    if (i >= args.length) {\n                        break;\n                    }\n                    continue;\n                }\n\n                // boolean parameters don't have values, default to false\n                if (assignment.param.type.name === 'boolean') {\n                    assignment.setValue(true);\n                }\n                else {\n                    if (i + 1 < args.length) {\n                        // Missing value portion of this named param\n                        this._hints.push(new Hint(Status.INCOMPLETE,\n                                'Missing value for: ' + namedArgText,\n                                args[i]));\n                    }\n                    else {\n                        args.splice(i + 1, 1);\n                        assignment.setArgument(args[i + 1]);\n                    }\n                }\n\n                lang.arrayRemove(names, assignment.name);\n                args.splice(i, 1);\n                // We don't need to i++ if we splice\n            }\n        }, this);\n\n        // What's left are positional parameters assign in order\n        names.forEach(function(name) {\n            var assignment = this.getAssignment(name);\n            if (args.length === 0) {\n                // No more values\n                assignment.setValue(undefined); // i.e. default\n            }\n            else {\n                var arg = args[0];\n                args.splice(0, 1);\n                assignment.setArgument(arg);\n            }\n        }, this);\n\n        if (args.length > 0) {\n            var remaining = Argument.merge(args);\n            this._hints.push(new Hint(Status.INVALID,\n                    'Input \\'' + remaining.text + '\\' makes no sense.',\n                    remaining));\n        }\n    };\n\n})();\nexports.CliRequisition = CliRequisition;\n\n\n});\n/* ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Mozilla Skywriter.\n *\n * The Initial Developer of the Original Code is\n * Mozilla.\n * Portions created by the Initial Developer are Copyright (C) 2009\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *   Joe Walker (jwalker@mozilla.com)\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('cockpit/ui/settings', function(require, exports, module) {\n\n\nvar types = require(\"pilot/types\");\nvar SelectionType = require('pilot/types/basic').SelectionType;\n\nvar direction = new SelectionType({\n    name: 'direction',\n    data: [ 'above', 'below' ]\n});\n\nvar hintDirectionSetting = {\n    name: \"hintDirection\",\n    description: \"Are hints shown above or below the command line?\",\n    type: \"direction\",\n    defaultValue: \"above\"\n};\n\nvar outputDirectionSetting = {\n    name: \"outputDirection\",\n    description: \"Is the output window shown above or below the command line?\",\n    type: \"direction\",\n    defaultValue: \"above\"\n};\n\nvar outputHeightSetting = {\n    name: \"outputHeight\",\n    description: \"What height should the output panel be?\",\n    type: \"number\",\n    defaultValue: 300\n};\n\nexports.startup = function(data, reason) {\n    types.registerType(direction);\n    data.env.settings.addSetting(hintDirectionSetting);\n    data.env.settings.addSetting(outputDirectionSetting);\n    data.env.settings.addSetting(outputHeightSetting);\n};\n\nexports.shutdown = function(data, reason) {\n    types.unregisterType(direction);\n    data.env.settings.removeSetting(hintDirectionSetting);\n    data.env.settings.removeSetting(outputDirectionSetting);\n    data.env.settings.removeSetting(outputHeightSetting);\n};\n\n\n});\n/* ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Skywriter.\n *\n * The Initial Developer of the Original Code is\n * Mozilla.\n * Portions created by the Initial Developer are Copyright (C) 2009\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *   Joe Walker (jwalker@mozilla.com)\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('cockpit/ui/cli_view', function(require, exports, module) {\n\n\nvar editorCss = require(\"text!cockpit/ui/cli_view.css\");\nvar event = require(\"pilot/event\");\nvar dom = require(\"pilot/dom\");\n\ndom.importCssString(editorCss);\n\nvar event = require(\"pilot/event\");\nvar keys = require(\"pilot/keys\");\nvar canon = require(\"pilot/canon\");\nvar Status = require('pilot/types').Status;\n\nvar CliRequisition = require('cockpit/cli').CliRequisition;\nvar Hint = require('cockpit/cli').Hint;\nvar RequestView = require('cockpit/ui/request_view').RequestView;\n\nvar NO_HINT = new Hint(Status.VALID, '', 0, 0);\n\n/**\n * On startup we need to:\n * 1. Add 3 sets of elements to the DOM for:\n * - command line output\n * - input hints\n * - completion\n * 2. Attach a set of events so the command line works\n */\nexports.startup = function(data, reason) {\n    var cli = new CliRequisition(data.env);\n    var cliView = new CliView(cli, data.env);\n    data.env.cli = cli;\n};\n\n/**\n * A class to handle the simplest UI implementation\n */\nfunction CliView(cli, env) {\n    cli.cliView = this;\n    this.cli = cli;\n    this.doc = document;\n    this.win = dom.getParentWindow(this.doc);\n    this.env = env;\n\n    // TODO: we should have a better way to specify command lines???\n    this.element = this.doc.getElementById('cockpitInput');\n    if (!this.element) {\n        // console.log('No element with an id of cockpit. Bailing on cli');\n        return;\n    }\n\n    this.settings = env.settings;\n    this.hintDirection = this.settings.getSetting('hintDirection');\n    this.outputDirection = this.settings.getSetting('outputDirection');\n    this.outputHeight = this.settings.getSetting('outputHeight');\n\n    // If the requisition tells us something has changed, we use this to know\n    // if we should ignore it\n    this.isUpdating = false;\n\n    this.createElements();\n    this.update();\n}\nCliView.prototype = {\n    /**\n     * Create divs for completion, hints and output\n     */\n    createElements: function() {\n        var input = this.element;\n\n        this.element.spellcheck = false;\n\n        this.output = this.doc.getElementById('cockpitOutput');\n        this.popupOutput = (this.output == null);\n        if (!this.output) {\n            this.output = this.doc.createElement('div');\n            this.output.id = 'cockpitOutput';\n            this.output.className = 'cptOutput';\n            input.parentNode.insertBefore(this.output, input.nextSibling);\n\n            var setMaxOutputHeight = function() {\n                this.output.style.maxHeight = this.outputHeight.get() + 'px';\n            }.bind(this);\n            this.outputHeight.addEventListener('change', setMaxOutputHeight);\n            setMaxOutputHeight();\n        }\n\n        this.completer = this.doc.createElement('div');\n        this.completer.className = 'cptCompletion VALID';\n\n        this.completer.style.color = dom.computedStyle(input, \"color\");\n        this.completer.style.fontSize = dom.computedStyle(input, \"fontSize\");\n        this.completer.style.fontFamily = dom.computedStyle(input, \"fontFamily\");\n        this.completer.style.fontWeight = dom.computedStyle(input, \"fontWeight\");\n        this.completer.style.fontStyle = dom.computedStyle(input, \"fontStyle\");\n        input.parentNode.insertBefore(this.completer, input.nextSibling);\n\n        // Transfer background styling to the completer.\n        this.completer.style.backgroundColor = input.style.backgroundColor;\n        input.style.backgroundColor = 'transparent';\n\n        this.hinter = this.doc.createElement('div');\n        this.hinter.className = 'cptHints';\n        input.parentNode.insertBefore(this.hinter, input.nextSibling);\n\n        var resizer = this.resizer.bind(this);\n        event.addListener(this.win, 'resize', resizer);\n        this.hintDirection.addEventListener('change', resizer);\n        this.outputDirection.addEventListener('change', resizer);\n        resizer();\n\n        canon.addEventListener('output',  function(ev) {\n            new RequestView(ev.request, this);\n        }.bind(this));\n        event.addCommandKeyListener(input, this.onCommandKey.bind(this));\n        event.addListener(input, 'keyup', this.onKeyUp.bind(this));\n\n        // cursor position affects hint severity. TODO: shortcuts for speed\n        event.addListener(input, 'mouseup', function(ev) {\n            this.isUpdating = true;\n            this.update();\n            this.isUpdating = false;\n        }.bind(this));\n\n        this.cli.addEventListener('argumentChange', this.onArgChange.bind(this));\n\n        event.addListener(input, \"focus\", function() {\n            dom.addCssClass(this.output, \"cptFocusPopup\");\n            dom.addCssClass(this.hinter, \"cptFocusPopup\");\n        }.bind(this));\n\n        function hideOutput() {\n            dom.removeCssClass(this.output, \"cptFocusPopup\");\n            dom.removeCssClass(this.hinter, \"cptFocusPopup\");\n        };\n        event.addListener(input, \"blur\", hideOutput.bind(this));\n        hideOutput.call(this);\n    },\n\n    /**\n     * We need to see the output of the latest command entered\n     */\n    scrollOutputToBottom: function() {\n        // Certain browsers have a bug such that scrollHeight is too small\n        // when content does not fill the client area of the element\n        var scrollHeight = Math.max(this.output.scrollHeight, this.output.clientHeight);\n        this.output.scrollTop = scrollHeight - this.output.clientHeight;\n    },\n\n    /**\n     * To be called on window resize or any time we want to align the elements\n     * with the input box.\n     */\n    resizer: function() {\n        var rect = this.element.getClientRects()[0];\n\n        this.completer.style.top = rect.top + 'px';\n        var height = rect.bottom - rect.top;\n        this.completer.style.height = height + 'px';\n        this.completer.style.lineHeight = height + 'px';\n        this.completer.style.left = rect.left + 'px';\n        var width = rect.right - rect.left;\n        this.completer.style.width = width + 'px';\n\n        if (this.hintDirection.get() === 'below') {\n            this.hinter.style.top = rect.bottom + 'px';\n            this.hinter.style.bottom = 'auto';\n        }\n        else {\n            this.hinter.style.top = 'auto';\n            this.hinter.style.bottom = (this.doc.documentElement.clientHeight - rect.top) + 'px';\n        }\n        this.hinter.style.left = (rect.left + 30) + 'px';\n        this.hinter.style.maxWidth = (width - 110) + 'px';\n\n        if (this.popupOutput) {\n            if (this.outputDirection.get() === 'below') {\n                this.output.style.top = rect.bottom + 'px';\n                this.output.style.bottom = 'auto';\n            }\n            else {\n                this.output.style.top = 'auto';\n                this.output.style.bottom = (this.doc.documentElement.clientHeight - rect.top) + 'px';\n            }\n            this.output.style.left = rect.left + 'px';\n            this.output.style.width = (width - 80) + 'px';\n        }\n    },\n\n    /**\n     * Ensure that TAB isn't handled by the browser\n     */\nonCommandKey: function(ev, hashId, keyCode) {\n        var stopEvent;\n        if (keyCode === keys.TAB ||\n                keyCode === keys.UP ||\n                keyCode === keys.DOWN) {\n            stopEvent = true;\n        } else if (hashId != 0 || keyCode != 0) {\n            stopEvent = canon.execKeyCommand(this.env, 'cli', hashId, keyCode);\n        }\n        stopEvent && event.stopEvent(ev);\n    },\n\n    /**\n     * The main keyboard processing loop\n     */\n    onKeyUp: function(ev) {\n        var handled;\n        /*\n        var handled = keyboardManager.processKeyEvent(ev, this, {\n            isCommandLine: true, isKeyUp: true\n        });\n        */\n\n        // RETURN does a special exec/highlight thing\n        if (ev.keyCode === keys.RETURN) {\n            var worst = this.cli.getWorstHint();\n            // Deny RETURN unless the command might work\n            if (worst.status === Status.VALID) {\n                this.cli.exec();\n                this.element.value = '';\n            }\n            else {\n                // If we've denied RETURN because the command was not VALID,\n                // select the part of the command line that is causing problems\n                // TODO: if there are 2 errors are we picking the right one?\n                dom.setSelectionStart(this.element, worst.start);\n                dom.setSelectionEnd(this.element, worst.end);\n            }\n        }\n\n        this.update();\n\n        // Special actions which delegate to the assignment\n        var current = this.cli.getAssignmentAt(dom.getSelectionStart(this.element));\n        if (current) {\n            // TAB does a special complete thing\n            if (ev.keyCode === keys.TAB) {\n                current.complete();\n                this.update();\n            }\n\n            // UP/DOWN look for some history\n            if (ev.keyCode === keys.UP) {\n                current.increment();\n                this.update();\n            }\n            if (ev.keyCode === keys.DOWN) {\n                current.decrement();\n                this.update();\n            }\n        }\n\n        return handled;\n    },\n\n    /**\n     * Actually parse the input and make sure we're all up to date\n     */\n    update: function() {\n        this.isUpdating = true;\n        var input = {\n            typed: this.element.value,\n            cursor: {\n                start: dom.getSelectionStart(this.element),\n                end: dom.getSelectionEnd(this.element.selectionEnd)\n            }\n        };\n        this.cli.update(input);\n\n        var display = this.cli.getAssignmentAt(input.cursor.start).getHint();\n\n        // 1. Update the completer with prompt/error marker/TAB info\n        dom.removeCssClass(this.completer, Status.VALID.toString());\n        dom.removeCssClass(this.completer, Status.INCOMPLETE.toString());\n        dom.removeCssClass(this.completer, Status.INVALID.toString());\n\n        var completion = '<span class=\"cptPrompt\">&gt;</span> ';\n        if (this.element.value.length > 0) {\n            var scores = this.cli.getInputStatusMarkup();\n            completion += this.markupStatusScore(scores);\n        }\n\n        // Display the \"-> prediction\" at the end of the completer\n        if (this.element.value.length > 0 &&\n                display.predictions && display.predictions.length > 0) {\n            var tab = display.predictions[0];\n            completion += ' &nbsp;&#x21E5; ' + (tab.name ? tab.name : tab);\n        }\n        this.completer.innerHTML = completion;\n        dom.addCssClass(this.completer, this.cli.getWorstHint().status.toString());\n\n        // 2. Update the hint element\n        var hint = '';\n        if (this.element.value.length !== 0) {\n            hint += display.message;\n            if (display.predictions && display.predictions.length > 0) {\n                hint += ': [ ';\n                display.predictions.forEach(function(prediction) {\n                    hint += (prediction.name ? prediction.name : prediction);\n                    hint += ' | ';\n                }, this);\n                hint = hint.replace(/\\| $/, ']');\n            }\n        }\n\n        this.hinter.innerHTML = hint;\n        if (hint.length === 0) {\n            dom.addCssClass(this.hinter, 'cptNoPopup');\n        }\n        else {\n            dom.removeCssClass(this.hinter, 'cptNoPopup');\n        }\n\n        this.isUpdating = false;\n    },\n\n    /**\n     * Markup an array of Status values with spans\n     */\n    markupStatusScore: function(scores) {\n        var completion = '';\n        // Create mark-up\n        var i = 0;\n        var lastStatus = -1;\n        while (true) {\n            if (lastStatus !== scores[i]) {\n                completion += '<span class=' + scores[i].toString() + '>';\n                lastStatus = scores[i];\n            }\n            completion += this.element.value[i];\n            i++;\n            if (i === this.element.value.length) {\n                completion += '</span>';\n                break;\n            }\n            if (lastStatus !== scores[i]) {\n                completion += '</span>';\n            }\n        }\n\n        return completion;\n    },\n\n    /**\n     * Update the input element to reflect the changed argument\n     */\n    onArgChange: function(ev) {\n        if (this.isUpdating) {\n            return;\n        }\n\n        var prefix = this.element.value.substring(0, ev.argument.start);\n        var suffix = this.element.value.substring(ev.argument.end);\n        var insert = typeof ev.text === 'string' ? ev.text : ev.text.name;\n        this.element.value = prefix + insert + suffix;\n        // Fix the cursor.\n        var insertEnd = (prefix + insert).length;\n        this.element.selectionStart = insertEnd;\n        this.element.selectionEnd = insertEnd;\n    }\n};\nexports.CliView = CliView;\n\n\n});\n/* ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Skywriter.\n *\n * The Initial Developer of the Original Code is\n * Mozilla.\n * Portions created by the Initial Developer are Copyright (C) 2009\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *   Joe Walker (jwalker@mozilla.com)\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('cockpit/ui/request_view', function(require, exports, module) {\n\nvar dom = require(\"pilot/dom\");\nvar event = require(\"pilot/event\");\nvar requestViewHtml = require(\"text!cockpit/ui/request_view.html\");\nvar Templater = require(\"pilot/domtemplate\").Templater;\n\nvar requestViewCss = require(\"text!cockpit/ui/request_view.css\");\ndom.importCssString(requestViewCss);\n\n/**\n * Pull the HTML into the DOM, but don't add it to the document\n */\nvar templates = document.createElement('div');\ntemplates.innerHTML = requestViewHtml;\nvar row = templates.querySelector('.cptRow');\n\n/**\n * Work out the path for images.\n * TODO: This should probably live in some utility area somewhere\n */\nfunction imageUrl(path) {\n    var dataUrl;\n    try {\n        dataUrl = require('text!cockpit/ui/' + path);\n    } catch (e) { }\n    if (dataUrl) {\n        return dataUrl;\n    }\n\n    var filename = module.id.split('/').pop() + '.js';\n    var imagePath;\n\n    if (module.uri.substr(-filename.length) !== filename) {\n        console.error('Can\\'t work out path from module.uri/module.id');\n        return path;\n    }\n\n    if (module.uri) {\n        var end = module.uri.length - filename.length - 1;\n        return module.uri.substr(0, end) + \"/\" + path;\n    }\n\n    return filename + path;\n}\n\n\n/**\n * Adds a row to the CLI output display\n */\nfunction RequestView(request, cliView) {\n    this.request = request;\n    this.cliView = cliView;\n    this.imageUrl = imageUrl;\n\n    // Elements attached to this by the templater. For info only\n    this.rowin = null;\n    this.rowout = null;\n    this.output = null;\n    this.hide = null;\n    this.show = null;\n    this.duration = null;\n    this.throb = null;\n\n    new Templater().processNode(row.cloneNode(true), this);\n\n    this.cliView.output.appendChild(this.rowin);\n    this.cliView.output.appendChild(this.rowout);\n\n    this.request.addEventListener('output', this.onRequestChange.bind(this));\n};\n\nRequestView.prototype = {\n    /**\n     * A single click on an invocation line in the console copies the command to\n     * the command line\n     */\n    copyToInput: function() {\n        this.cliView.element.value = this.request.typed;\n    },\n\n    /**\n     * A double click on an invocation line in the console executes the command\n     */\n    executeRequest: function(ev) {\n        this.cliView.cli.update({\n            typed: this.request.typed,\n            cursor: { start:0, end:0 }\n        });\n        this.cliView.cli.exec();\n    },\n\n    hideOutput: function(ev) {\n        this.output.style.display = 'none';\n        dom.addCssClass(this.hide, 'cmd_hidden');\n        dom.removeCssClass(this.show, 'cmd_hidden');\n\n        event.stopPropagation(ev);\n    },\n\n    showOutput: function(ev) {\n        this.output.style.display = 'block';\n        dom.removeCssClass(this.hide, 'cmd_hidden');\n        dom.addCssClass(this.show, 'cmd_hidden');\n\n        event.stopPropagation(ev);\n    },\n\n    remove: function(ev) {\n        this.cliView.output.removeChild(this.rowin);\n        this.cliView.output.removeChild(this.rowout);\n        event.stopPropagation(ev);\n    },\n\n    onRequestChange: function(ev) {\n        this.duration.innerHTML = this.request.duration ?\n            'completed in ' + (this.request.duration / 1000) + ' sec ' :\n            '';\n\n        this.output.innerHTML = '';\n        this.request.outputs.forEach(function(output) {\n            var node;\n            if (typeof output == 'string') {\n                node = document.createElement('p');\n                node.innerHTML = output;\n            } else {\n                node = output;\n            }\n            this.output.appendChild(node);\n        }, this);\n        this.cliView.scrollOutputToBottom();\n\n        dom.setCssClass(this.output, 'cmd_error', this.request.error);\n\n        this.throb.style.display = this.request.completed ? 'none' : 'block';\n    }\n};\nexports.RequestView = RequestView;\n\n\n});\n/* ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is DomTemplate.\n *\n * The Initial Developer of the Original Code is Mozilla.\n * Portions created by the Initial Developer are Copyright (C) 2009\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *   Joe Walker (jwalker@mozilla.com) (original author)\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('pilot/domtemplate', function(require, exports, module) {\n\n\n// WARNING: do not 'use_strict' without reading the notes in envEval;\n\n/**\n * A templater that allows one to quickly template DOM nodes.\n */\nfunction Templater() {\n  this.scope = [];\n};\n\n/**\n * Recursive function to walk the tree processing the attributes as it goes.\n * @param node the node to process. If you pass a string in instead of a DOM\n * element, it is assumed to be an id for use with document.getElementById()\n * @param data the data to use for node processing.\n */\nTemplater.prototype.processNode = function(node, data) {\n  if (typeof node === 'string') {\n    node = document.getElementById(node);\n  }\n  if (data === null || data === undefined) {\n    data = {};\n  }\n  this.scope.push(node.nodeName + (node.id ? '#' + node.id : ''));\n  try {\n    // Process attributes\n    if (node.attributes && node.attributes.length) {\n      // We need to handle 'foreach' and 'if' first because they might stop\n      // some types of processing from happening, and foreach must come first\n      // because it defines new data on which 'if' might depend.\n      if (node.hasAttribute('foreach')) {\n        this.processForEach(node, data);\n        return;\n      }\n      if (node.hasAttribute('if')) {\n        if (!this.processIf(node, data)) {\n          return;\n        }\n      }\n      // Only make the node available once we know it's not going away\n      data.__element = node;\n      // It's good to clean up the attributes when we've processed them,\n      // but if we do it straight away, we mess up the array index\n      var attrs = Array.prototype.slice.call(node.attributes);\n      for (var i = 0; i < attrs.length; i++) {\n        var value = attrs[i].value;\n        var name = attrs[i].name;\n        this.scope.push(name);\n        try {\n          if (name === 'save') {\n            // Save attributes are a setter using the node\n            value = this.stripBraces(value);\n            this.property(value, data, node);\n            node.removeAttribute('save');\n          } else if (name.substring(0, 2) === 'on') {\n            // Event registration relies on property doing a bind\n            value = this.stripBraces(value);\n            var func = this.property(value, data);\n            if (typeof func !== 'function') {\n              this.handleError('Expected ' + value +\n                ' to resolve to a function, but got ' + typeof func);\n            }\n            node.removeAttribute(name);\n            var capture = node.hasAttribute('capture' + name.substring(2));\n            node.addEventListener(name.substring(2), func, capture);\n            if (capture) {\n              node.removeAttribute('capture' + name.substring(2));\n            }\n          } else {\n            // Replace references in all other attributes\n            var self = this;\n            var newValue = value.replace(/\\$\\{[^}]*\\}/g, function(path) {\n              return self.envEval(path.slice(2, -1), data, value);\n            });\n            // Remove '_' prefix of attribute names so the DOM won't try\n            // to use them before we've processed the template\n            if (name.charAt(0) === '_') {\n              node.removeAttribute(name);\n              node.setAttribute(name.substring(1), newValue);\n            } else if (value !== newValue) {\n              attrs[i].value = newValue;\n            }\n          }\n        } finally {\n          this.scope.pop();\n        }\n      }\n    }\n\n    // Loop through our children calling processNode. First clone them, so the\n    // set of nodes that we visit will be unaffected by additions or removals.\n    var childNodes = Array.prototype.slice.call(node.childNodes);\n    for (var j = 0; j < childNodes.length; j++) {\n      this.processNode(childNodes[j], data);\n    }\n\n    if (node.nodeType === Node.TEXT_NODE) {\n      this.processTextNode(node, data);\n    }\n  } finally {\n    this.scope.pop();\n  }\n};\n\n/**\n * Handle <x if=\"${...}\">\n * @param node An element with an 'if' attribute\n * @param data The data to use with envEval\n * @returns true if processing should continue, false otherwise\n */\nTemplater.prototype.processIf = function(node, data) {\n  this.scope.push('if');\n  try {\n    var originalValue = node.getAttribute('if');\n    var value = this.stripBraces(originalValue);\n    var recurse = true;\n    try {\n      var reply = this.envEval(value, data, originalValue);\n      recurse = !!reply;\n    } catch (ex) {\n      this.handleError('Error with \\'' + value + '\\'', ex);\n      recurse = false;\n    }\n    if (!recurse) {\n      node.parentNode.removeChild(node);\n    }\n    node.removeAttribute('if');\n    return recurse;\n  } finally {\n    this.scope.pop();\n  }\n};\n\n/**\n * Handle <x foreach=\"param in ${array}\"> and the special case of\n * <loop foreach=\"param in ${array}\">\n * @param node An element with a 'foreach' attribute\n * @param data The data to use with envEval\n */\nTemplater.prototype.processForEach = function(node, data) {\n  this.scope.push('foreach');\n  try {\n    var originalValue = node.getAttribute('foreach');\n    var value = originalValue;\n\n    var paramName = 'param';\n    if (value.charAt(0) === '$') {\n      // No custom loop variable name. Use the default: 'param'\n      value = this.stripBraces(value);\n    } else {\n      // Extract the loop variable name from 'NAME in ${ARRAY}'\n      var nameArr = value.split(' in ');\n      paramName = nameArr[0].trim();\n      value = this.stripBraces(nameArr[1].trim());\n    }\n    node.removeAttribute('foreach');\n    try {\n      var self = this;\n      // Process a single iteration of a loop\n      var processSingle = function(member, clone, ref) {\n        ref.parentNode.insertBefore(clone, ref);\n        data[paramName] = member;\n        self.processNode(clone, data);\n        delete data[paramName];\n      };\n\n      // processSingle is no good for <loop> nodes where we want to work on\n      // the childNodes rather than the node itself\n      var processAll = function(scope, member) {\n        self.scope.push(scope);\n        try {\n          if (node.nodeName === 'LOOP') {\n            for (var i = 0; i < node.childNodes.length; i++) {\n              var clone = node.childNodes[i].cloneNode(true);\n              processSingle(member, clone, node);\n            }\n          } else {\n            var clone = node.cloneNode(true);\n            clone.removeAttribute('foreach');\n            processSingle(member, clone, node);\n          }\n        } finally {\n          self.scope.pop();\n        }\n      };\n\n      var reply = this.envEval(value, data, originalValue);\n      if (Array.isArray(reply)) {\n        reply.forEach(function(data, i) {\n          processAll('' + i, data);\n        }, this);\n      } else {\n        for (var param in reply) {\n          if (reply.hasOwnProperty(param)) {\n            processAll(param, param);\n          }\n        }\n      }\n      node.parentNode.removeChild(node);\n    } catch (ex) {\n      this.handleError('Error with \\'' + value + '\\'', ex);\n    }\n  } finally {\n    this.scope.pop();\n  }\n};\n\n/**\n * Take a text node and replace it with another text node with the ${...}\n * sections parsed out. We replace the node by altering node.parentNode but\n * we could probably use a DOM Text API to achieve the same thing.\n * @param node The Text node to work on\n * @param data The data to use in calls to envEval\n */\nTemplater.prototype.processTextNode = function(node, data) {\n  // Replace references in other attributes\n  var value = node.data;\n  // We can't use the string.replace() with function trick (see generic\n  // attribute processing in processNode()) because we need to support\n  // functions that return DOM nodes, so we can't have the conversion to a\n  // string.\n  // Instead we process the string as an array of parts. In order to split\n  // the string up, we first replace '${' with '\\uF001$' and '}' with '\\uF002'\n  // We can then split using \\uF001 or \\uF002 to get an array of strings\n  // where scripts are prefixed with $.\n  // \\uF001 and \\uF002 are just unicode chars reserved for private use.\n  value = value.replace(/\\$\\{([^}]*)\\}/g, '\\uF001$$$1\\uF002');\n  var parts = value.split(/\\uF001|\\uF002/);\n  if (parts.length > 1) {\n    parts.forEach(function(part) {\n      if (part === null || part === undefined || part === '') {\n        return;\n      }\n      if (part.charAt(0) === '$') {\n        part = this.envEval(part.slice(1), data, node.data);\n      }\n      // It looks like this was done a few lines above but see envEval\n      if (part === null) {\n        part = \"null\";\n      }\n      if (part === undefined) {\n        part = \"undefined\";\n      }\n      // if (isDOMElement(part)) { ... }\n      if (typeof part.cloneNode !== 'function') {\n        part = node.ownerDocument.createTextNode(part.toString());\n      }\n      node.parentNode.insertBefore(part, node);\n    }, this);\n    node.parentNode.removeChild(node);\n  }\n};\n\n/**\n * Warn of string does not begin '${' and end '}'\n * @param str the string to check.\n * @return The string stripped of ${ and }, or untouched if it does not match\n */\nTemplater.prototype.stripBraces = function(str) {\n  if (!str.match(/\\$\\{.*\\}/g)) {\n    this.handleError('Expected ' + str + ' to match ${...}');\n    return str;\n  }\n  return str.slice(2, -1);\n};\n\n/**\n * Combined getter and setter that works with a path through some data set.\n * For example:\n * <ul>\n * <li>property('a.b', { a: { b: 99 }}); // returns 99\n * <li>property('a', { a: { b: 99 }}); // returns { b: 99 }\n * <li>property('a', { a: { b: 99 }}, 42); // returns 99 and alters the\n * input data to be { a: { b: 42 }}\n * </ul>\n * @param path An array of strings indicating the path through the data, or\n * a string to be cut into an array using <tt>split('.')</tt>\n * @param data An object to look in for the <tt>path</tt> argument\n * @param newValue (optional) If defined, this value will replace the\n * original value for the data at the path specified.\n * @return The value pointed to by <tt>path</tt> before any\n * <tt>newValue</tt> is applied.\n */\nTemplater.prototype.property = function(path, data, newValue) {\n  this.scope.push(path);\n  try {\n    if (typeof path === 'string') {\n      path = path.split('.');\n    }\n    var value = data[path[0]];\n    if (path.length === 1) {\n      if (newValue !== undefined) {\n        data[path[0]] = newValue;\n      }\n      if (typeof value === 'function') {\n        return function() {\n          return value.apply(data, arguments);\n        };\n      }\n      return value;\n    }\n    if (!value) {\n      this.handleError('Can\\'t find path=' + path);\n      return null;\n    }\n    return this.property(path.slice(1), value, newValue);\n  } finally {\n    this.scope.pop();\n  }\n};\n\n/**\n * Like eval, but that creates a context of the variables in <tt>env</tt> in\n * which the script is evaluated.\n * WARNING: This script uses 'with' which is generally regarded to be evil.\n * The alternative is to create a Function at runtime that takes X parameters\n * according to the X keys in the env object, and then call that function using\n * the values in the env object. This is likely to be slow, but workable.\n * @param script The string to be evaluated.\n * @param env The environment in which to eval the script.\n * @param context Optional debugging string in case of failure\n * @return The return value of the script, or the error message if the script\n * execution failed.\n */\nTemplater.prototype.envEval = function(script, env, context) {\n  with (env) {\n    try {\n      this.scope.push(context);\n      return eval(script);\n    } catch (ex) {\n      this.handleError('Template error evaluating \\'' + script + '\\'', ex);\n      return script;\n    } finally {\n      this.scope.pop();\n    }\n  }\n};\n\n/**\n * A generic way of reporting errors, for easy overloading in different\n * environments.\n * @param message the error message to report.\n * @param ex optional associated exception.\n */\nTemplater.prototype.handleError = function(message, ex) {\n  this.logError(message);\n  this.logError('In: ' + this.scope.join(' > '));\n  if (ex) {\n    this.logError(ex);\n  }\n};\n\n\n/**\n * A generic way of reporting errors, for easy overloading in different\n * environments.\n * @param message the error message to report.\n */\nTemplater.prototype.logError = function(message) {\n  window.console && window.console.log && console.log(message);\n};\n\nexports.Templater = Templater;\n\n\n});\n/* ***** BEGIN LICENSE BLOCK *****\n * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n *\n * The contents of this file are subject to the Mozilla Public License Version\n * 1.1 (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n * http://www.mozilla.org/MPL/\n *\n * Software distributed under the License is distributed on an \"AS IS\" basis,\n * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n * for the specific language governing rights and limitations under the\n * License.\n *\n * The Original Code is Skywriter.\n *\n * The Initial Developer of the Original Code is\n * Mozilla.\n * Portions created by the Initial Developer are Copyright (C) 2009\n * the Initial Developer. All Rights Reserved.\n *\n * Contributor(s):\n *   Skywriter Team (skywriter@mozilla.com)\n *\n * Alternatively, the contents of this file may be used under the terms of\n * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n * in which case the provisions of the GPL or the LGPL are applicable instead\n * of those above. If you wish to allow use of your version of this file only\n * under the terms of either the GPL or the LGPL, and not to allow others to\n * use your version of this file under the terms of the MPL, indicate your\n * decision by deleting the provisions above and replace them with the notice\n * and other provisions required by the GPL or the LGPL. If you do not delete\n * the provisions above, a recipient may use your version of this file under\n * the terms of any one of the MPL, the GPL or the LGPL.\n *\n * ***** END LICENSE BLOCK ***** */\n\ndefine('cockpit/commands/basic', function(require, exports, module) {\n\n\nvar canon = require('pilot/canon');\n\n/**\n * '!' command\n */\nvar bangCommandSpec = {\n    name: 'sh',\n    description: 'Execute a system command (requires server support)',\n    params: [\n        {\n            name: 'command',\n            type: 'text',\n            description: 'The string to send to the os shell.'\n        }\n    ],\n    exec: function(env, args, request) {\n        var req = new XMLHttpRequest();\n        req.open('GET', '/exec?args=' + args.command, true);\n        req.onreadystatechange = function(ev) {\n          if (req.readyState == 4) {\n            if (req.status == 200) {\n              request.done('<pre>' + req.responseText + '</pre>');\n            }\n          }\n        };\n        req.send(null);\n    }\n};\n\nvar canon = require('pilot/canon');\n\nexports.startup = function(data, reason) {\n    canon.addCommand(bangCommandSpec);\n};\n\nexports.shutdown = function(data, reason) {\n    canon.removeCommand(bangCommandSpec);\n};\n\n\n});\ndefine(\"text!cockpit/ui/cli_view.css\", \"\" +\n  \"#cockpitInput { padding-left: 16px; }\" +\n  \"\" +\n  \".cptOutput { overflow: auto; position: absolute; z-index: 999; display: none; }\" +\n  \"\" +\n  \".cptCompletion { padding: 0; position: absolute; z-index: -1000; }\" +\n  \".cptCompletion.VALID { background: #FFF; }\" +\n  \".cptCompletion.INCOMPLETE { background: #DDD; }\" +\n  \".cptCompletion.INVALID { background: #DDD; }\" +\n  \".cptCompletion span { color: #FFF; }\" +\n  \".cptCompletion span.INCOMPLETE { color: #DDD; border-bottom: 2px dotted #F80; }\" +\n  \".cptCompletion span.INVALID { color: #DDD; border-bottom: 2px dotted #F00; }\" +\n  \"span.cptPrompt { color: #66F; font-weight: bold; }\" +\n  \"\" +\n  \"\" +\n  \".cptHints {\" +\n  \"  color: #000;\" +\n  \"  position: absolute;\" +\n  \"  border: 1px solid rgba(230, 230, 230, 0.8);\" +\n  \"  background: rgba(250, 250, 250, 0.8);\" +\n  \"  -moz-border-radius-topleft: 10px;\" +\n  \"  -moz-border-radius-topright: 10px;\" +\n  \"  border-top-left-radius: 10px; border-top-right-radius: 10px;\" +\n  \"  z-index: 1000;\" +\n  \"  padding: 8px;\" +\n  \"  display: none;\" +\n  \"}\" +\n  \"\" +\n  \".cptFocusPopup { display: block; }\" +\n  \".cptFocusPopup.cptNoPopup { display: none; }\" +\n  \"\" +\n  \".cptHints ul { margin: 0; padding: 0 15px; }\" +\n  \"\" +\n  \".cptGt { font-weight: bold; font-size: 120%; }\" +\n  \"\");\n\ndefine(\"text!cockpit/ui/request_view.css\", \"\" +\n  \".cptRowIn {\" +\n  \"  display: box; display: -moz-box; display: -webkit-box;\" +\n  \"  box-orient: horizontal; -moz-box-orient: horizontal; -webkit-box-orient: horizontal;\" +\n  \"  box-align: center; -moz-box-align: center; -webkit-box-align: center;\" +\n  \"  color: #333;\" +\n  \"  background-color: #EEE;\" +\n  \"  width: 100%;\" +\n  \"  font-family: consolas, courier, monospace;\" +\n  \"}\" +\n  \".cptRowIn > * { padding-left: 2px; padding-right: 2px; }\" +\n  \".cptRowIn > img { cursor: pointer; }\" +\n  \".cptHover { display: none; }\" +\n  \".cptRowIn:hover > .cptHover { display: block; }\" +\n  \".cptRowIn:hover > .cptHover.cptHidden { display: none; }\" +\n  \".cptOutTyped {\" +\n  \"  box-flex: 1; -moz-box-flex: 1; -webkit-box-flex: 1;\" +\n  \"  font-weight: bold; color: #000; font-size: 120%;\" +\n  \"}\" +\n  \".cptRowOutput { padding-left: 10px; line-height: 1.2em; }\" +\n  \".cptRowOutput strong,\" +\n  \".cptRowOutput b,\" +\n  \".cptRowOutput th,\" +\n  \".cptRowOutput h1,\" +\n  \".cptRowOutput h2,\" +\n  \".cptRowOutput h3 { color: #000; }\" +\n  \".cptRowOutput a { font-weight: bold; color: #666; text-decoration: none; }\" +\n  \".cptRowOutput a: hover { text-decoration: underline; cursor: pointer; }\" +\n  \".cptRowOutput input[type=password],\" +\n  \".cptRowOutput input[type=text],\" +\n  \".cptRowOutput textarea {\" +\n  \"  color: #000; font-size: 120%;\" +\n  \"  background: transparent; padding: 3px;\" +\n  \"  border-radius: 5px; -moz-border-radius: 5px; -webkit-border-radius: 5px;\" +\n  \"}\" +\n  \".cptRowOutput table,\" +\n  \".cptRowOutput td,\" +\n  \".cptRowOutput th { border: 0; padding: 0 2px; }\" +\n  \".cptRowOutput .right { text-align: right; }\" +\n  \"\");\n\ndefine(\"text!cockpit/ui/request_view.html\", \"\" +\n  \"<div class=cptRow>\" +\n  \"  <!-- The div for the input (i.e. what was typed) -->\" +\n  \"  <div class=\\\"cptRowIn\\\" save=\\\"${rowin}\\\"\" +\n  \"      onclick=\\\"${copyToInput}\\\"\" +\n  \"      ondblclick=\\\"${executeRequest}\\\">\" +\n  \"\" +\n  \"    <!-- What the user actually typed -->\" +\n  \"    <div class=\\\"cptGt\\\">&gt; </div>\" +\n  \"    <div class=\\\"cptOutTyped\\\">${request.typed}</div>\" +\n  \"\" +\n  \"    <!-- The extra details that appear on hover -->\" +\n  \"    <div class=cptHover save=\\\"${duration}\\\"></div>\" +\n  \"    <img class=cptHover onclick=\\\"${hideOutput}\\\" save=\\\"${hide}\\\"\" +\n  \"        alt=\\\"Hide command output\\\" _src=\\\"${imageUrl('images/minus.png')}\\\"/>\" +\n  \"    <img class=\\\"cptHover cptHidden\\\" onclick=\\\"${showOutput}\\\" save=\\\"${show}\\\"\" +\n  \"        alt=\\\"Show command output\\\" _src=\\\"${imageUrl('images/plus.png')}\\\"/>\" +\n  \"    <img class=cptHover onclick=\\\"${remove}\\\"\" +\n  \"        alt=\\\"Remove this command from the history\\\"\" +\n  \"        _src=\\\"${imageUrl('images/closer.png')}\\\"/>\" +\n  \"\" +\n  \"  </div>\" +\n  \"\" +\n  \"  <!-- The div for the command output -->\" +\n  \"  <div class=\\\"cptRowOut\\\" save=\\\"${rowout}\\\">\" +\n  \"    <div class=\\\"cptRowOutput\\\" save=\\\"${output}\\\"></div>\" +\n  \"    <img _src=\\\"${imageUrl('images/throbber.gif')}\\\" save=\\\"${throb}\\\"/>\" +\n  \"  </div>\" +\n  \"</div>\" +\n  \"\");\n\ndefine(\"text!cockpit/ui/images/closer.png\", \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAj9JREFUeNp0ks+LUlEUx7/vV1o8Z8wUx3IEHcQmiBiQlomjRNCiZpEuEqF/oEUwq/6EhvoHggmRcJUQBM1CRJAW0aLIaGQimZJxJsWxyV/P9/R1zzWlFl04vPvOPZ9z7rnnK5imidmKRCIq+zxgdoPZ1T/ut8xeM3tcKpW6s1hhBkaj0Qj7bDebTX+324WmadxvsVigqipcLleN/d4rFoulORiLxTZY8ItOp8MBCpYkiYPj8Xjus9vtlORWoVB4KcTjcQc732dLpSRXvCZaAws6Q4WDdqsO52kNH+oCRFGEz+f7ydwBKRgMPmTXi49GI1x2D/DsznesB06ws2eDbI7w9HYN6bVjvGss4KAjwDAMq81mM2SW5Wa/3weBbz42UL9uYnVpiO2Nr9ANHSGXib2Wgm9tCYIggGKJEVkvlwgi5/FQRmTLxO6hgJVzI1x0T/fJrBtHJxPeL6tI/fsZLA6ot8lkQi8HRVbw94gkWYI5MaHrOjcCGSNRxZosy9y5cErDzn0Dqx7gcwO8WtBp4PndI35GMYqiUMUvBL5yOBz8yRfFNpbPmqgcCFh/IuHa1nR/YXGM8+oUpFhihEQiwcdRLpfVRqOBtWXWq34Gra6AXq8Hp2piZcmKT4cKnE4nwuHwdByVSmWQz+d32WCTlHG/qaHHREN9kgi0sYQfv0R4PB4EAgESQDKXy72fSy6VSnHJVatVf71eR7vd5n66mtfrRSgU4pLLZrOlf7RKK51Ok8g3/yPyR5lMZi7y3wIMAME4EigHWgKnAAAAAElFTkSuQmCC\");\n\ndefine(\"text!cockpit/ui/images/dot_clear.gif\", \"data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAEBMgA7\");\n\ndefine(\"text!cockpit/ui/images/minus.png\", \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAAZiS0dEANIA0gDS7KbF4AAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9kFGw4xMrIJw5EAAAHcSURBVCjPhZIxSxtxGMZ/976XhJA/RA5EAyJcFksnp64hjUPBoXRyCYLQTyD0UxScu0nFwalCQSgFCVk7dXAwUAiBDA2RO4W7yN1x9+9gcyhU+pteHt4H3pfncay1LOl0OgY4BN4Ar/7KP4BvwNFwOIyWu87S2O12O8DxfD73oygiSRIAarUaxhhWV1fHwMFgMBiWxl6v9y6Koi+3t7ckSUKtVkNVAcjzvNRWVlYwxry9vLz86uzs7HjAZDKZGGstjUaDfxHHMSLC5ubmHdB2VfVwNpuZ5clxHPMcRVFwc3PTXFtbO3RFZHexWJCmabnweAaoVqvlv4vFAhHZdVX1ZZqmOI5DURR8fz/lxbp9Yrz+7bD72SfPcwBU1XdF5N5aWy2KgqIoeBzPEnWVLMseYnAcRERdVR27rrsdxzGqyutP6898+GBsNBqo6i9XVS88z9sOggAR4X94noeqXoiIHPm+H9XrdYIgIAxDwjAkTVPCMESzBy3LMprNJr7v34nIkV5dXd2fn59fG2P2siwjSRIqlQrWWlSVJFcqlQqtVot2u40xZu/s7OxnWbl+v98BjkejkT+dTgmCoDxtY2ODra2tMXBweno6fNJVgP39fQN8eKbkH09OTsqS/wHFRdHPfTSfjwAAAABJRU5ErkJggg==\");\n\ndefine(\"text!cockpit/ui/images/pinaction.png\", \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAC7mlDQ1BJQ0MgUHJvZmlsZQAAeAGFVM9rE0EU/jZuqdAiCFprDrJ4kCJJWatoRdQ2/RFiawzbH7ZFkGQzSdZuNuvuJrWliOTi0SreRe2hB/+AHnrwZC9KhVpFKN6rKGKhFy3xzW5MtqXqwM5+8943731vdt8ADXLSNPWABOQNx1KiEWlsfEJq/IgAjqIJQTQlVdvsTiQGQYNz+Xvn2HoPgVtWw3v7d7J3rZrStpoHhP1A4Eea2Sqw7xdxClkSAog836Epx3QI3+PY8uyPOU55eMG1Dys9xFkifEA1Lc5/TbhTzSXTQINIOJT1cVI+nNeLlNcdB2luZsbIEL1PkKa7zO6rYqGcTvYOkL2d9H5Os94+wiHCCxmtP0a4jZ71jNU/4mHhpObEhj0cGDX0+GAVtxqp+DXCFF8QTSeiVHHZLg3xmK79VvJKgnCQOMpkYYBzWkhP10xu+LqHBX0m1xOv4ndWUeF5jxNn3tTd70XaAq8wDh0MGgyaDUhQEEUEYZiwUECGPBoxNLJyPyOrBhuTezJ1JGq7dGJEsUF7Ntw9t1Gk3Tz+KCJxlEO1CJL8Qf4qr8lP5Xn5y1yw2Fb3lK2bmrry4DvF5Zm5Gh7X08jjc01efJXUdpNXR5aseXq8muwaP+xXlzHmgjWPxHOw+/EtX5XMlymMFMXjVfPqS4R1WjE3359sfzs94i7PLrXWc62JizdWm5dn/WpI++6qvJPmVflPXvXx/GfNxGPiKTEmdornIYmXxS7xkthLqwviYG3HCJ2VhinSbZH6JNVgYJq89S9dP1t4vUZ/DPVRlBnM0lSJ93/CKmQ0nbkOb/qP28f8F+T3iuefKAIvbODImbptU3HvEKFlpW5zrgIXv9F98LZua6N+OPwEWDyrFq1SNZ8gvAEcdod6HugpmNOWls05Uocsn5O66cpiUsxQ20NSUtcl12VLFrOZVWLpdtiZ0x1uHKE5QvfEp0plk/qv8RGw/bBS+fmsUtl+ThrWgZf6b8C8/UXAeIuJAAAACXBIWXMAAAsTAAALEwEAmpwYAAAClklEQVQ4EX1TXUhUQRQ+Z3Zmd+9uN1q2P3UpZaEwcikKekkqLKggKHJ96MHe9DmLkCDa9U198Id8kErICmIlRAN96UdE6QdBW/tBA5Uic7E0zN297L17p5mb1zYjD3eYc+d83zlnON8g5xzWNUSEdUBkHTJasRWySPP7fw3hfwkk2GoNsc0vOaJRHo1GV/GiMctkTIJRFlpZli8opK+htmf83gXeG63oteOtra0u25e7TYJIJELb26vYCACTgUe1lXV86BTn745l+MsyHqs53S/Aq4VEUa9Y6ko14eYY4u3AyM3HYwdKU35DZyblGR2+qq6W0X2Nnh07xynnVYpHORx/E1/GvvqaAZUayjMjdM2f/Lgr5E+fV93zR4u3zKCLughsZqKwAzAxaz6dPY6JgjLUF+eSP5OpjmAw2E8DvldHSvJMKPg08aRor1tc4BuALu6mOwGWdQC3mKIqRsC8mKd8wYfD78/earzSYzdMDW9QgKb0Is8CBY1mQXOiaXAHEpMDE5XTJqIq4EiyxUqKlpfkF0pyV1OTAoFAhmTmyCCoDsZNZvIkUjELQpipo0sQqYZAswZHwsEEE10M0pq2SSZY9HqNcDicJcNTpBvQJz40UbSOTh1B8bDpuY0w9Hb3kkn9lPAlBLfhfD39XTtX/blFJqiqrjbkTi63Hbofj2uL4GMsmzFgbDJ/vmMgv/lB4syJ0oXO7d3j++vio6GFsYmD6cHJreWc3/jRVVHhsOYvM8iZ36mtjPDBk/xDZE8CoHlbrlAssbTxDdDJvdb536L7I6S7Vy++6Gi4Xi9BsUthJRaLOYSPz4XALKI4j4iObd/e5UtDKUjZzYyYRyGAJv01Zj8kC5cbs5WY83hQnv0DzCXl+r8APElkq0RU6oMAAAAASUVORK5CYII=\");\n\ndefine(\"text!cockpit/ui/images/pinin.png\", \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAC7mlDQ1BJQ0MgUHJvZmlsZQAAeAGFVM9rE0EU/jZuqdAiCFprDrJ4kCJJWatoRdQ2/RFiawzbH7ZFkGQzSdZuNuvuJrWliOTi0SreRe2hB/+AHnrwZC9KhVpFKN6rKGKhFy3xzW5MtqXqwM5+8943731vdt8ADXLSNPWABOQNx1KiEWlsfEJq/IgAjqIJQTQlVdvsTiQGQYNz+Xvn2HoPgVtWw3v7d7J3rZrStpoHhP1A4Eea2Sqw7xdxClkSAog836Epx3QI3+PY8uyPOU55eMG1Dys9xFkifEA1Lc5/TbhTzSXTQINIOJT1cVI+nNeLlNcdB2luZsbIEL1PkKa7zO6rYqGcTvYOkL2d9H5Os94+wiHCCxmtP0a4jZ71jNU/4mHhpObEhj0cGDX0+GAVtxqp+DXCFF8QTSeiVHHZLg3xmK79VvJKgnCQOMpkYYBzWkhP10xu+LqHBX0m1xOv4ndWUeF5jxNn3tTd70XaAq8wDh0MGgyaDUhQEEUEYZiwUECGPBoxNLJyPyOrBhuTezJ1JGq7dGJEsUF7Ntw9t1Gk3Tz+KCJxlEO1CJL8Qf4qr8lP5Xn5y1yw2Fb3lK2bmrry4DvF5Zm5Gh7X08jjc01efJXUdpNXR5aseXq8muwaP+xXlzHmgjWPxHOw+/EtX5XMlymMFMXjVfPqS4R1WjE3359sfzs94i7PLrXWc62JizdWm5dn/WpI++6qvJPmVflPXvXx/GfNxGPiKTEmdornIYmXxS7xkthLqwviYG3HCJ2VhinSbZH6JNVgYJq89S9dP1t4vUZ/DPVRlBnM0lSJ93/CKmQ0nbkOb/qP28f8F+T3iuefKAIvbODImbptU3HvEKFlpW5zrgIXv9F98LZua6N+OPwEWDyrFq1SNZ8gvAEcdod6HugpmNOWls05Uocsn5O66cpiUsxQ20NSUtcl12VLFrOZVWLpdtiZ0x1uHKE5QvfEp0plk/qv8RGw/bBS+fmsUtl+ThrWgZf6b8C8/UXAeIuJAAAACXBIWXMAAAsTAAALEwEAmpwYAAABZ0lEQVQ4Ea2TPUsDQRCGZ89Eo4FACkULEQs1CH4Uamfjn7GxEYJFIFXgChFsbPwzNnZioREkaiHBQtEiEEiMRm/dZ8OEGAxR4sBxx877Pju7M2estTJIxLrNuVwuMxQEx0ZkzcFHyRtjXt02559RtB2GYanTYzoryOfz+6l4Nbszf2niwffKmpGRo9sVW22mDgqFwp5C2gDMm+P32a3JB1N+n5JifUGeP9JeNxGryPLYjcwMP8rJ07Q9fZltQzyAstOJ2vVu5sKc1ZZkRBrOcKeb+HexPidvkpCN5JUcllZtpZFc5DgBWc5M2eysZuMuofMBSA4NWjx4PUCsXefMlI0QY3ewRg4NWi4ZTQsgrjYXema+e4VqtEMK6KXvu+4B9Bklt90vVKMeD2BI6DOt4rZ/Gk7WyKFBi4fNPIAJY0joM61SCCZ9tI1o0OIB8D+DBIkYaJRbCBH9mZgNt+bb++ufSSF/eX8BYcDeAzuQJVUAAAAASUVORK5CYII=\");\n\ndefine(\"text!cockpit/ui/images/pinout.png\", \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAC7mlDQ1BJQ0MgUHJvZmlsZQAAeAGFVM9rE0EU/jZuqdAiCFprDrJ4kCJJWatoRdQ2/RFiawzbH7ZFkGQzSdZuNuvuJrWliOTi0SreRe2hB/+AHnrwZC9KhVpFKN6rKGKhFy3xzW5MtqXqwM5+8943731vdt8ADXLSNPWABOQNx1KiEWlsfEJq/IgAjqIJQTQlVdvsTiQGQYNz+Xvn2HoPgVtWw3v7d7J3rZrStpoHhP1A4Eea2Sqw7xdxClkSAog836Epx3QI3+PY8uyPOU55eMG1Dys9xFkifEA1Lc5/TbhTzSXTQINIOJT1cVI+nNeLlNcdB2luZsbIEL1PkKa7zO6rYqGcTvYOkL2d9H5Os94+wiHCCxmtP0a4jZ71jNU/4mHhpObEhj0cGDX0+GAVtxqp+DXCFF8QTSeiVHHZLg3xmK79VvJKgnCQOMpkYYBzWkhP10xu+LqHBX0m1xOv4ndWUeF5jxNn3tTd70XaAq8wDh0MGgyaDUhQEEUEYZiwUECGPBoxNLJyPyOrBhuTezJ1JGq7dGJEsUF7Ntw9t1Gk3Tz+KCJxlEO1CJL8Qf4qr8lP5Xn5y1yw2Fb3lK2bmrry4DvF5Zm5Gh7X08jjc01efJXUdpNXR5aseXq8muwaP+xXlzHmgjWPxHOw+/EtX5XMlymMFMXjVfPqS4R1WjE3359sfzs94i7PLrXWc62JizdWm5dn/WpI++6qvJPmVflPXvXx/GfNxGPiKTEmdornIYmXxS7xkthLqwviYG3HCJ2VhinSbZH6JNVgYJq89S9dP1t4vUZ/DPVRlBnM0lSJ93/CKmQ0nbkOb/qP28f8F+T3iuefKAIvbODImbptU3HvEKFlpW5zrgIXv9F98LZua6N+OPwEWDyrFq1SNZ8gvAEcdod6HugpmNOWls05Uocsn5O66cpiUsxQ20NSUtcl12VLFrOZVWLpdtiZ0x1uHKE5QvfEp0plk/qv8RGw/bBS+fmsUtl+ThrWgZf6b8C8/UXAeIuJAAAACXBIWXMAAAsTAAALEwEAmpwYAAACyUlEQVQ4EW1TXUgUURQ+Z3ZmnVV3QV2xJbVSEIowQbAfLQx8McLoYX2qjB58MRSkP3vZppceYhGxgrZaIughlYpE7CHFWiiKyj9II0qxWmwlNh1Xtp2f27mz7GDlZX7uuXO+73zfuXeQMQYIgAyALppgyBtse32stsw86txkHhATn+FbfPfzxnPB+vR3RMJYuTwW6bbB4a6WS5O3Yu2VlXIesDiAamiQNKVlVXfx5I0GJ7DY7p0/+erU4dgeMJIA31WNxZmAgibOreXDqF55sY4SFUURqbi+nkjgwTyAbHhLX8yOLsSM2QRA3JRAAgd4RGPbVhkKEp8qeJ7PFyW3fw++YHtC7CkaD0amqyqihSwlMQQ0wa07IjPVI/vbexreIUrVaQV2D4RMQ/o7m12Mdfx4H3PfB9FNzTR1U2cO0Bi45aV6xNvFBNaoIAfbSiwLlqi9/hR/R3Nrhua+Oqi9TEKiB02C7YXz+Pba4MTDrpbLiMAxNgmXb+HpwVkZdoIrkn9isW7nRw/TZYaagZArAWyhfqsSDL/c9aTx7JUjGZCtYExRqCzAwGblwr6aFQ84nTo6qZ7XCeCVQNckE/KSWolvoQnxeoFFgIh8G/nA+kBAxxuQO5m9eFrwLIGJHgcyM63VFMhRSgNVyJr7og8y1vbTQpH8DIEVgxuYuexw0QECIalq5FYgEmpkgoFYltU/lnrqDz5osirSFpF7lrHAFKSWHYfEs+mY/82UnAStyMlW8sUPsVIciTZgz3jV1ebg0CEOpgPF22s1z1YQYKSXPJ1hbAhR8T26WdLhkuVfAzPR+YO1Ox5n58SmCcF6e3uzAoHA77RkevJdWH/3+f2O9TGf3w3fWQ2Hw5F/13mcsWAT+vv6DK4kFApJ/d3d1k+kJtbCrmxXHS3n8ER6b3CQbAqaEHVra6sGxcXW4SovLx+empxapS//FfwD9kpMJjMMBBAAAAAASUVORK5CYII=\");\n\ndefine(\"text!cockpit/ui/images/pins.png\", \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAQCAYAAABQrvyxAAAACXBIWXMAAAsTAAALEwEAmpwYAAAGYklEQVRIDbVWe0yURxCf/R735o6DO0FBe0RFsaL4iLXGIKa2SY3P6JGa2GpjlJjUV9NosbU++tYUbEnaQIrVaKJBG7WiNFQFUWO1UUEsVg2CAgoeHHLewcH32O58cBdQsX9Y5+7LfrszOzO/2ZnZj1BKgTBiIwVGVvKd49OVVYunDlXn6wdBKh+ogXrv+DOz1melIb+3LM5fNv2XPYE5EHY+L3PJljN5zavHpJjsQNsA/JJEgyC2+WTjy3b0GfoJW8O4aoHtDwiHQrj5lw1LLyyb1bp5zAjJTus9klrVpdD6TqH2ngVO+0dsRJnp06cLIYU4fx7NnRI3bu7UIYOeJ/McnuY88q3k62gc0S4Dgf5qhICQtIXS2lqD7BhSduPk3YfyzXaANhBBJDxYdUqCywB2qS4RdyUuSkTF/VJxcbH5j8N7/75RuFrN3Zh8OS8zqf5m4UpPeenOyP42dbtBeuvVnCdkK1e4PfPouX03mo9se+c33M8wqDk5Ofqed8REUTicQhbySUxp9u3KlMSHTtrFU6Kyn03lz15PPpW25vsZeYSIKyiVURcqeZJOH9lTNZLfnxRjU/uwrjbEUBWsapcSO2Hq4k0VfZg9EzxdDNCEjDxgNqRDme9umz/btwlsHRIEePHgAf73RdnHZ6LTuIUBN7OBQ+c1Fdnp6cZ1BQUdeRuWZi97o3ktDQQkVeFFzqJARd1A5a0Vr7ta6Kp6TZjtZ+NTIOoKF6qDrL7e0QQIUCiqMMKk8Z1Q/SCSKvzocf2B6NEN0SQn/kTO6fKJ0zqjZUlQBSpJ0GjR77w0aoc1Pr6S5/kVJrNpakV5hR+LWKN4t7sLX+p0rx2vqSta64olIulUKUgCSXLWE1R4KPPSj+5vhm2hdDOG+CkQBmhhyyKq6SaFYWTn5bB3QJRNz54AuXKn8TJjhu0Wbv+wNEKQjVhnmKopjo4FxXmetCRnC4F7BhCiCUepqAepRh0TM/gjjzOOSK2NgWZPc05qampRWJHb7dbOffep2ednzLzgczlbrQA6gHYF9BYDh9GY+FjddMweHMscmMuep07gXlMQoqw9ALoYu5MJsak9QmJA2IvAgVmoCRciooyPujJtNCv1uHt3TmK9gegFKrG9kh6oXwZiIEAtBIjORGKNTWR/WeW8XVkbjuJepLAyloM8LmTN//njKZPbraATZaLjCHEww9Ei4FFiPg6Ja5gT6gxYgLgnRDHRQwJXbz2GOw0d4A3K4GXlUtMahJjYVxiYbrwOmxIS10bFnIBOSi6Tl9Jgs0zbOEX18wyEwgLPMrxD1Y4aCK8kmTpgYcpAF27Mzs42Hjx4kA8BICUlJfKArR7LcEvTB1xEC9AoEw9OPagWkVU/D1oesmK6U911zEczMVe01oZjiMggg6ux2Qk379qh4rYKet4GjrhhwEteBgBrH8BssoXEtbHzPpSBRRSpqlNpgAiUoxzHKxLRszoVuggIisxaDQWZqkQvQjAoax3NbDbLLGuUEABNGedXqSyLRupXgDT5JfAGZNLio9B0X8Uiwk4w77MDc1D4yejjWtykPS3DX01UDCY/GPQcVDe0QYT0CIxGFvUorfvBxZsRfVrUuWruMBAb/lXCUofoFNZfzGJtowXOX0vwUSFK4BgyMKm6P6s9wQUZld+jrYyMDC0iIQDaJdG4IyZQfL3RfbFcCBIlRgc+u3CjaTApuZ9KsANgG8PNzHlWWD3tCxd6kafNNiFp5HAalAkkJ0SCV2H3CgOD9Nc/FqrXuyb0Eocvfhq171p5eyuJ1omKJEP5rQGe/FOOnXtq335z8YmvYo9cHb2t8spIb3lVSseZW46FlGY/Sk9P50P2w20UlWJUkUHIushfc5PXGAzCo0PlD2pnpCYfCXga3lu+fPlevEhWrVrFyrN/Orfv87FOW9tlqb2Kc9pV8DzioMk3UNUbXM+8B/ATBr8C8CKdvGXWGD/9sqm3dkxtzA4McMjHMB8D2ftheYXo+qzt3pXvz8/PP/vk+v8537V+yYW87Zu+RZ1ZbrexoKAA/SBpaWn4+aL5w5zGk+/jW59JiMkESW5urpiVlWXENRb1H/Yf2I9txIxz5IdkX3TsraukpsbQjz6090yb4XsAvQoRE0YvJdamtIIbOnRoUVlZ2ftsLVQzIdEXHntsaZdimssVfCpFui109+BnWPsXaWLI/zactygAAAAASUVORK5CYII=\");\n\ndefine(\"text!cockpit/ui/images/plus.png\", \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAAZiS0dEANIA0gDS7KbF4AAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9kFGw4yFTwuJTkAAAH7SURBVCjPdZKxa1NRFMZ/956XZMgFyyMlCZRA4hBx6lBcQ00GoYi4tEstFPwLAs7iLDi7FWuHThaUggihBDI5OWRoQAmBQFISQgvvpbwX3rsOaR4K+o2H8zvfOZxPWWtZqVarGaAJPAEe3ZW/A1+Bd+1221v1qhW4vb1dA44mk0nZ8zyCIAAgk8lgjGF9fb0PHF5cXLQTsF6vP/c879P19TVBEJDJZBARAKIoSmpra2sYY561Wq3PqtFouMBgMBgYay3ZbJZ/yfd9tNaUSqUboOKISPPq6sqsVvZ9H4AvL34B8PTj/QSO45jpdHovn883Ha31znw+JwzDpCEMQx4UloM8zyOdTif3zudztNY7jog8DMMQpRRxHPPt5TCBAEZvxlyOFTsfykRRBICIlB2t9a21Nh3HMXEc8+d7VhJHWCwWyzcohdZaHBHpO46z6fs+IsLj94XECaD4unCHL8FsNouI/HRE5Nx13c3ZbIbWOnG5HKtl+53TSq7rIiLnand31wUGnU7HjEYjlFLJZN/3yRnL1FMYY8jlcmxtbd0AFel2u7dnZ2eXxpi9xWJBEASkUimstYgIQSSkUimKxSKVSgVjzN7p6emPJHL7+/s14KjX65WHwyGz2SxZbWNjg2q12gcOT05O2n9lFeDg4MAAr/4T8rfHx8dJyH8DvvbYGzKvWukAAAAASUVORK5CYII=\");\n\ndefine(\"text!cockpit/ui/images/throbber.gif\", \"data:image/gif;base64,R0lGODlh3AATAPQAAP///wAAAL6+vqamppycnLi4uLKyssjIyNjY2MTExNTU1Nzc3ODg4OTk5LCwsLy8vOjo6Ozs7MrKyvLy8vT09M7Ozvb29sbGxtDQ0O7u7tbW1sLCwqqqqvj4+KCgoJaWliH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAA3AATAAAF/yAgjmRpnmiqrmzrvnAsz3Rt33iu73zv/8CgcEgECAaEpHLJbDqf0Kh0Sq1ar9isdjoQtAQFg8PwKIMHnLF63N2438f0mv1I2O8buXjvaOPtaHx7fn96goR4hmuId4qDdX95c4+RG4GCBoyAjpmQhZN0YGYFXitdZBIVGAoKoq4CG6Qaswi1CBtkcG6ytrYJubq8vbfAcMK9v7q7D8O1ycrHvsW6zcTKsczNz8HZw9vG3cjTsMIYqQgDLAQGCQoLDA0QCwUHqfYSFw/xEPz88/X38Onr14+Bp4ADCco7eC8hQYMAEe57yNCew4IVBU7EGNDiRn8Z831cGLHhSIgdE/9chIeBgDoB7gjaWUWTlYAFE3LqzDCTlc9WOHfm7PkTqNCh54rePDqB6M+lR536hCpUqs2gVZM+xbrTqtGoWqdy1emValeXKwgcWABB5y1acFNZmEvXwoJ2cGfJrTv3bl69Ffj2xZt3L1+/fw3XRVw4sGDGcR0fJhxZsF3KtBTThZxZ8mLMgC3fRatCLYMIFCzwLEprg84OsDus/tvqdezZf13Hvr2B9Szdu2X3pg18N+68xXn7rh1c+PLksI/Dhe6cuO3ow3NfV92bdArTqC2Ebc3A8vjf5QWf15Bg7Nz17c2fj69+fnq+8N2Lty+fuP78/eV2X13neIcCeBRwxorbZrAxAJoCDHbgoG8RTshahQ9iSKEEzUmYIYfNWViUhheCGJyIP5E4oom7WWjgCeBBAJNv1DVV01MZdJhhjdkplWNzO/5oXI846njjVEIqR2OS2B1pE5PVscajkxhMycqLJgxQCwT40PjfAV4GqNSXYdZXJn5gSkmmmmJu1aZYb14V51do+pTOCmA00AqVB4hG5IJ9PvYnhIFOxmdqhpaI6GeHCtpooisuutmg+Eg62KOMKuqoTaXgicQWoIYq6qiklmoqFV0UoeqqrLbq6quwxirrrLTWauutJ4QAACH5BAkKAAAALAAAAADcABMAAAX/ICCOZGmeaKqubOu+cCzPdG3feK7vfO//wKBwSAQIBoSkcslsOp/QqHRKrVqv2Kx2OhC0BAXHx/EoCzboAcdhcLDdgwJ6nua03YZ8PMFPoBMca215eg98G36IgYNvDgOGh4lqjHd7fXOTjYV9nItvhJaIfYF4jXuIf4CCbHmOBZySdoOtj5eja59wBmYFXitdHhwSFRgKxhobBgUPAmdoyxoI0tPJaM5+u9PaCQZzZ9gP2tPcdM7L4tLVznPn6OQb18nh6NV0fu3i5OvP8/nd1qjwaasHcIPAcf/gBSyAAMMwBANYEAhWYQGDBhAyLihwYJiEjx8fYMxIcsGDAxVA/yYIOZIkBAaGPIK8INJlRpgrPeasaRPmx5QgJfB0abLjz50tSeIM+pFmUo0nQQIV+vRlTJUSnNq0KlXCSq09ozIFexEBAYkeNiwgOaEtn2LFpGEQsKCtXbcSjOmVlqDuhAx3+eg1Jo3u37sZBA9GoMAw4MB5FyMwfLht4sh7G/utPGHlYAV8Nz9OnOBz4c2VFWem/Pivar0aKCP2LFn2XwhnVxBwsPbuBAQbEGiIFg1BggoWkidva5z4cL7IlStfkED48OIYoiufYIH68+cKPkqfnsB58ePjmZd3Dj199/XE20tv6/27XO3S6z9nPCz9BP3FISDefL/Bt192/uWmAv8BFzAQAQUWWFaaBgqA11hbHWTIXWIVXifNhRlq6FqF1sm1QQYhdiAhbNEYc2KKK1pXnAIvhrjhBh0KxxiINlqQAY4UXjdcjSJyeAx2G2BYJJD7NZQkjCPKuCORKnbAIXsuKhlhBxEomAIBBzgIYXIfHfmhAAyMR2ZkHk62gJoWlNlhi33ZJZ2cQiKTJoG05Wjcm3xith9dcOK5X51tLRenoHTuud2iMnaolp3KGXrdBo7eKYF5p/mXgJcogClmcgzAR5gCKymXYqlCgmacdhp2UCqL96mq4nuDBTmgBasaCFp4sHaQHHUsGvNRiiGyep1exyIra2mS7dprrtA5++z/Z8ZKYGuGsy6GqgTIDvupRGE+6CO0x3xI5Y2mOTkBjD4ySeGU79o44mcaSEClhglgsKyJ9S5ZTGY0Bnzrj+3SiKK9Rh5zjAALCywZBk/ayCWO3hYM5Y8Dn6qxxRFsgAGoJwwgDQRtYXAAragyQOmaLKNZKGaEuUlpyiub+ad/KtPqpntypvvnzR30DBtjMhNodK6Eqrl0zU0/GjTUgG43wdN6Ra2pAhGtAAZGE5Ta8TH6wknd2IytNKaiZ+Or79oR/tcvthIcAPe7DGAs9Edwk6r3qWoTaNzY2fb9HuHh2S343Hs1VIHhYtOt+Hh551rh24vP5YvXSGzh+eeghy76GuikU9FFEainrvrqrLfu+uuwxy777LTXfkIIACH5BAkKAAAALAAAAADcABMAAAX/ICCOZGmeaKqubOu+cCzPdG3feK7vfO//wKBwSAQIBoSkcslsOp/QqHRKrVqv2Kx2OhC0BAWHB2l4CDZo9IDjcBja7UEhTV+3DXi3PJFA8xMcbHiDBgMPG31pgHBvg4Z9iYiBjYx7kWocb26OD398mI2EhoiegJlud4UFiZ5sm6Kdn2mBr5t7pJ9rlG0cHg5gXitdaxwFGArIGgoaGwYCZ3QFDwjU1AoIzdCQzdPV1c0bZ9vS3tUJBmjQaGXl1OB0feze1+faiBvk8wjnimn55e/o4OtWjp+4NPIKogsXjaA3g/fiGZBQAcEAFgQGOChgYEEDCCBBLihwQILJkxIe/3wMKfJBSQkJYJpUyRIkgwcVUJq8QLPmTYoyY6ZcyfJmTp08iYZc8MBkhZgxk9aEcPOlzp5FmwI9KdWn1qASurJkClRoWKwhq6IUqpJBAwQEMBYroAHkhLt3+RyzhgCDgAV48Wbgg+waAnoLMgTOm6DwQ8CLBzdGdvjw38V5JTg2lzhyTMeUEwBWHPgzZc4TSOM1bZia6LuqJxCmnOxv7NSsl1mGHHiw5tOuIWeAEHcFATwJME/ApgFBc3MVLEgPvE+Ddb4JokufPmFBAuvPXWu3MIF89wTOmxvOvp179evQtwf2nr6aApPyzVd3jn089e/8xdfeXe/xdZ9/d1ngHf98lbHH3V0LMrgPgsWpcFwBEFBgHmyNXWeYAgLc1UF5sG2wTHjIhNjBiIKZCN81GGyQwYq9uajeMiBOQGOLJ1KjTI40kmfBYNfc2NcGIpI4pI0vyrhjiT1WFqOOLEIZnjVOVpmajYfBiCSNLGbA5YdOkjdihSkQwIEEEWg4nQUmvYhYe+bFKaFodN5lp3rKvJYfnBKAJ+gGDMi3mmbwWYfng7IheuWihu5p32XcSWdSj+stkF95dp64jJ+RBipocHkCCp6PCiRQ6INookCAAwy0yd2CtNET3Yo7RvihBjFZAOaKDHT43DL4BQnsZMo8xx6uI1oQrHXXhHZrB28G62n/YSYxi+uzP2IrgbbHbiaer7hCiOxDFWhrbmGnLVuus5NFexhFuHLX6gkEECorlLpZo0CWJG4pLjIACykmBsp0eSSVeC15TDJeUhlkowlL+SWLNJpW2WEF87urXzNWSZ6JOEb7b8g1brZMjCg3ezBtWKKc4MvyEtwybPeaMAA1ECRoAQYHYLpbeYYCLfQ+mtL5c9CnfQpYpUtHOSejEgT9ogZ/GSqd0f2m+LR5WzOtHqlQX1pYwpC+WbXKqSYtpJ5Mt4a01lGzS3akF60AxkcTaLgAyRBPWCoDgHfJqwRuBuzdw/1ml3iCwTIeLUWJN0v4McMe7uasCTxseNWPSxc5RbvIgD7geZLbGrqCG3jepUmbbze63Y6fvjiOylbwOITPfIHEFsAHL/zwxBdvPBVdFKH88sw37/zz0Ecv/fTUV2/99SeEAAAh+QQJCgAAACwAAAAA3AATAAAF/yAgjmRpnmiqrmzrvnAsz3Rt33iu73zv/8CgcEgECAaEpHLJbDqf0Kh0Sq1ar9isdjoQtAQFh2cw8BQEm3T6yHEYHHD4oKCuD9qGvNsxT6QTgAkcHHmFeX11fm17hXwPG35qgnhxbwMPkXaLhgZ9gWp3bpyegX4DcG+inY+Qn6eclpiZkHh6epetgLSUcBxlD2csXXdvBQrHGgoaGhsGaIkFDwjTCArTzX+QadHU3c1ofpHc3dcGG89/4+TYktvS1NYI7OHu3fEJ5tpqBu/k+HX7+nXDB06SuoHm0KXhR65cQT8P3FRAMIAFgVMPwDCAwLHjggIHJIgceeFBg44eC/+ITCCBZYKSJ1FCWPBgpE2YMmc+qNCypwScMmnaXAkUJYOaFVyKLOqx5tCXJnMelcBzJNSYKIX2ZPkzqsyjPLku9Zr1QciVErYxaICAgEUOBRJIgzChbt0MLOPFwyBggV27eCUcmxZvg9+/dfPGo5bg8N/Ag61ZM4w4seDF1fpWhizZmoa+GSortgcaMWd/fkP/HY0MgWbTipVV++wY8GhvqSG4XUEgoYTKE+Qh0OCvggULiBckWEZ4Ggbjx5HXVc58IPQJ0idQJ66XanTpFraTe348+XLizRNcz658eHMN3rNPT+C+G/nodqk3t6a+fN3j+u0Xn3nVTQPfdRPspkL/b+dEIN8EeMm2GAYbTNABdrbJ1hyFFv5lQYTodSZABhc+loCEyhxTYYkZopdMMiNeiBxyIFajV4wYHpfBBspUl8yKHu6ooV5APsZjQxyyeNeJ3N1IYod38cgdPBUid6GCKfRWgAYU4IccSyHew8B3doGJHmMLkGkZcynKk2Z50Ym0zJzLbDCmfBbI6eIyCdyJmJmoqZmnBAXy9+Z/yOlZDZpwYihnj7IZpuYEevrYJ5mJEuqiof4l+NYDEXQpXQcMnNjZNDx1oGqJ4S2nF3EsqWrhqqVWl6JIslpAK5MaIqDeqjJq56qN1aTaQaPbHTPYr8Be6Gsyyh6Da7OkmmqP/7GyztdrNVQBm5+pgw3X7aoYKhfZosb6hyUKBHCgQKij1rghkOAJuZg1SeYIIY+nIpDvf/sqm4yNG5CY64f87qdAwSXKGqFkhPH1ZHb2EgYtw3bpKGVkPz5pJAav+gukjB1UHE/HLNJobWcSX8jiuicMMBFd2OmKwQFs2tjXpDfnPE1j30V3c7iRHlrzBD2HONzODyZtsQJMI4r0AUNaE3XNHQw95c9GC001MpIxDacFQ+ulTNTZlU3O1eWVHa6vb/pnQUUrgHHSBKIuwG+bCPyEqbAg25gMVV1iOB/IGh5YOKLKIQ6xBAcUHmzjIcIqgajZ+Ro42DcvXl7j0U4WOUd+2IGu7DWjI1pt4DYq8BPm0entuGSQY/4tBi9Ss0HqfwngBQtHbCH88MQXb/zxyFfRRRHMN+/889BHL/301Fdv/fXYZ39CCAAh+QQJCgAAACwAAAAA3AATAAAF/yAgjmRpnmiqrmzrvnAsz3Rt33iu73zv/8CgcEgECAaEpHLJbDqf0Kh0Sq1ar9isdjoQtAQFh2fAKXsKm7R6Q+Y43vABep0mGwwOPH7w2CT+gHZ3d3lyagl+CQNvg4yGh36LcHoGfHR/ZYOElQ9/a4ocmoRygIiRk5p8pYmZjXePaYBujHoOqp5qZHBlHAUFXitddg8PBg8KGsgayxvGkAkFDwgICtPTzX2mftHW3QnOpojG3dbYkNjk1waxsdDS1N7ga9zw1t/aifTk35fu6Qj3numL14fOuHTNECHqU4DDgQEsCCwidiHBAwYQMmpcUOCAhI8gJVzUuLGThAQnP/9abEAyI4MCIVOKZNnyJUqUJxNcGNlywYOQgHZirGkSJ8gHNEky+AkS58qWEJYC/bMzacmbQHkqNdlUJ1KoSz2i9COhmQYCEXtVrCBgwYS3cCf8qTcNQ9u4cFFOq2bPLV65Cf7dxZthbjW+CgbjnWtNgWPFcAsHdoxgWWK/iyV045sAc2S96SDn1exYw17REwpLQEYt2eW/qtPZRQAB7QoC61RW+GsBwYZ/CXb/XRCYLsAKFizEtUAc+G7lcZsjroscOvTmsoUvx15PwccJ0N8yL17N9PG/E7jv9S4hOV7pdIPDdZ+ePDzv2qMXn2b5+wTbKuAWnF3oZbABZY0lVmD/ApQd9thybxno2GGuCVDggaUpoyBsB1bGGgIYbJCBcuFJiOAyGohIInQSmmdeiBnMF2GHfNUlIoc1rncjYRjW6NgGf3VQGILWwNjBfxEZcAFbC7gHXQcfUYOYdwzQNxo5yUhQZXhvRYlMeVSuSOJHKJa5AQMQThBlZWZ6Bp4Fa1qzTAJbijcBlJrtxeaZ4lnnpZwpukWieGQmYx5ATXIplwTL8DdNZ07CtWYybNIJF4Ap4NZHe0920AEDk035kafieQrqXofK5ympn5JHKYjPrfoWcR8WWQGp4Ul32KPVgXdnqxM6OKqspjIYrGPDrlrsZtRIcOuR86nHFwbPvmes/6PH4frrqbvySh+mKGhaAARPzjjdhCramdoGGOhp44i+zogBkSDuWC5KlE4r4pHJkarXrj++Raq5iLmWLlxHBteavjG+6amJrUkJJI4Ro5sBv9AaOK+jAau77sbH7nspCwNIYIACffL7J4JtWQnen421nNzMcB6AqpRa9klonmBSiR4GNi+cJZpvwgX0ejj71W9yR+eIgaVvQgf0l/A8nWjUFhwtZYWC4hVnkZ3p/PJqNQ5NnwUQrQCGBBBMQIGTtL7abK+5JjAv1fi9bS0GLlJHgdjEgYzzARTwC1fgEWdJuKKBZzj331Y23qB3i9v5aY/rSUC4w7PaLeWXmr9NszMFoN79eeiM232o33EJAIzaSGwh++y012777bhT0UURvPfu++/ABy/88MQXb/zxyCd/QggAIfkECQoAAAAsAAAAANwAEwAABf8gII5kaZ5oqq5s675wLM90bd94ru987//AoHBIBAgGhKRyyWw6n9CodEqtWq/YrHY6ELQEBY5nwCk7xIWNer0hO95wziC9Ttg5b4ND/+Y87IBqZAaEe29zGwmJigmDfHoGiImTjXiQhJEPdYyWhXwDmpuVmHwOoHZqjI6kZ3+MqhyemJKAdo6Ge3OKbEd4ZRwFBV4rc4MPrgYPChrMzAgbyZSJBcoI1tfQoYsJydfe2amT3d7W0OGp1OTl0YtqyQrq0Lt11PDk3KGoG+nxBpvTD9QhwCctm0BzbOyMIwdOUwEDEgawIOCB2oMLgB4wgMCx44IHBySIHClBY0ePfyT/JCB5weRJCAwejFw58kGDlzBTqqTZcuPLmCIBiWx58+VHmiRLFj0JVCVLl0xl7qSZwCbOo0lFWv0pdefQrVFDJtr5gMBEYBgxqBWwYILbtxPsqMPAFu7blfa81bUbN4HAvXAzyLWnoDBguHIRFF6m4LBbwQngMYPXuC3fldbyPrMcGLM3w5wRS1iWWUNlvnElKDZtz/EEwaqvYahQoexEfyILi4RrYYKFZwJ3810QWZ2ECrx9Ew+O3K6F5Yq9zXbb+y30a7olJJ+wnLC16W97Py+uwdtx1NcLWzs/3G9e07stVPc9kHJ0BcLtQp+c3ewKAgYkUAFpCaAmmHqKLSYA/18WHEiZPRhsQF1nlLFWmIR8ZbDBYs0YZuCGpGXWmG92aWiPMwhEOOEEHXRwIALlwXjhio+BeE15IzpnInaLbZBBhhti9x2GbnVQo2Y9ZuCfCgBeMCB+DJDIolt4iVhOaNSJdCOBUfIlkmkyMpPAAvKJ59aXzTQzJo0WoJnmQF36Jp6W1qC4gWW9GZladCiyJd+KnsHImgRRVjfnaDEKuiZvbcYWo5htzefbl5LFWNeSKQAo1QXasdhiiwwUl2B21H3aQaghXnPcp1NagCqYslXAqnV+zYWcpNwVp9l5eepJnHqL4SdBi56CGlmw2Zn6aaiZjZqfb8Y2m+Cz1O0n3f+tnvrGbF6kToApCgAWoNWPeh754JA0vmajiAr4iOuOW7abQXVGNriBWoRdOK8FxNqLwX3oluubhv8yluRbegqGb536ykesuoXhyJqPQJIGbLvQhkcwjKs1zBvBwSZIsbcsDCCBAAf4ya+UEhyQoIiEJtfoZ7oxUOafE2BwgMWMqUydfC1LVtiArk0QtGkWEopzlqM9aJrKHfw5c6wKjFkmXDrbhwFockodtMGFLWpXy9JdiXN1ZDNszV4WSLQCGBKoQYHUyonqrHa4ErewAgMmcAAF7f2baIoVzC2p3gUvJtLcvIWqloy6/R04mIpLwDhciI8qLOB5yud44pHPLbA83hFDWPjNbuk9KnySN57Av+TMBvgEAgzzNhJb5K777rz37vvvVHRRxPDEF2/88cgnr/zyzDfv/PPQnxACACH5BAkKAAAALAAAAADcABMAAAX/ICCOZGmeaKqubOu+cCzPdG3feK7vfO//wKBwSAQIBoSkcslsOp/QqHRKrVqv2Kx2OhC0BIUCwcMpO84OT2HDbm8GHLQjnn6wE3g83SA3DB55G3llfHxnfnZ4gglvew6Gf4ySgmYGlpCJknochWiId3kJcZZyDn93i6KPl4eniopwq6SIoZKxhpenbhtHZRxhXisDopwPgHkGDxrLGgjLG8mC0gkFDwjX2AgJ0bXJ2djbgNJsAtbfCNB2oOnn6MmKbeXt226K1fMGi6j359D69ua+QZskjd+3cOvY9XNgp4ABCQNYEDBl7EIeCQkeMIDAseOCBwckiBSZ4ILGjh4B/40kaXIjSggMHmBcifHky5gYE6zM2OAlzGM6Z5rs+fIjTZ0tfcYMSlLCUJ8fL47kCVXmTjwPiKJkUCDnyqc3CxzQmYeAxAEGLGJYiwCDgAUT4sqdgOebArdw507IUNfuW71xdZ7DC5iuhGsKErf9CxhPYgUaEhPWyzfBMgUIJDPW6zhb5M1y+R5GjFkBaLmCM0dOfHqvztXYJnMejaFCBQlmVxAYsEGkYnQV4lqYMNyCtnYSggNekAC58uJxmTufW5w55mwKkg+nLp105uTC53a/nhg88fMTmDfDVl65Xum/IZt/3/zaag3a5W63nll1dvfiWbaaZLmpQIABCVQA2f9lAhTG112PQWYadXE9+FtmEwKWwQYQJrZagxomsOCAGVImInsSbpCBhhwug6KKcXXQQYUcYuDMggrASFmNzjjzzIrh7cUhhhHqONeGpSEW2QYxHsmjhxpgUGAKB16g4IIbMNCkXMlhaJ8GWVJo2I3NyKclYF1GxgyYDEAnXHJrMpNAm/rFBSczPiYAlwXF8ZnmesvoOdyMbx7m4o0S5LWdn4bex2Z4xYmEzaEb5EUcnxbA+WWglqIn6aHPTInCgVbdlZyMqMrIQHMRSiaBBakS1903p04w434n0loBoQFOt1yu2YAnY68RXiNsqh2s2qqxuyKb7Imtmgcrqsp6h8D/fMSpapldx55nwayK/SfqCQd2hcFdAgDp5GMvqhvakF4mZuS710WGIYy30khekRkMu92GNu6bo7r/ttjqwLaua5+HOdrKq5Cl3dcwi+xKiLBwwwom4b0E6xvuYyqOa8IAEghwQAV45VvovpkxBl2mo0W7AKbCZXoAhgMmWnOkEqx2JX5nUufbgJHpXCfMOGu2QAd8eitpW1eaNrNeMGN27mNz0swziYnpSbXN19gYtstzfXrdYjNHtAIYGFVwwAEvR1dfxdjKxVzAP0twAAW/ir2w3nzTd3W4yQWO3t0DfleB4XYnEHCEhffdKgaA29p0eo4fHLng9qoG+OVyXz0gMeWGY7qq3xhiRIEAwayNxBawxy777LTXbjsVXRSh++689+7778AHL/zwxBdv/PEnhAAAIfkECQoAAAAsAAAAANwAEwAABf8gII5kaZ5oqq5s675wLM90bd94ru987//AoHBIBAgGhKRyyWw6n9CodEqtWq/YrHY6ELQEhYLD4BlwHGg0ubBpuzdm9Dk9eCTu+MTZkDb4PXYbeIIcHHxqf4F3gnqGY2kOdQmCjHCGfpCSjHhmh2N+knmEkJmKg3uHfgaaeY2qn6t2i4t7sKAPbwIJD2VhXisDCQZgDrKDBQ8aGgjKyhvDlJMJyAjV1gjCunkP1NfVwpRtk93e2ZVt5NfCk27jD97f0LPP7/Dr4pTp1veLgvrx7AL+Q/BM25uBegoYkDCABYFhEobhkUBRwoMGEDJqXPDgQMUEFC9c1LjxQUUJICX/iMRIEgIDkycrjmzJMSXFlDNJvkwJsmdOjQwKfDz5M+PLoSGLQqgZU6XSoB/voHxawGbFlS2XGktAwKEADB0xiEWAodqGBRPSqp1wx5qCamDRrp2Qoa3bagLkzrULF4GCvHPTglRAmKxZvWsHayBcliDitHUlvGWM97FgCdYWVw4c2e/kw4HZJlCwmDBhwHPrjraGYTHqtaoxVKggoesKAgd2SX5rbUMFCxOAC8cGDwHFwBYWJCgu4XfwtcqZV0grPHj0u2SnqwU+IXph3rK5b1fOu7Bx5+K7L6/2/Xhg8uyXnQ8dvfRiDe7TwyfNuzlybKYpgIFtKhAgwEKkKcOf/wChZbBBgMucRh1so5XH3wbI1WXafRJy9iCErmX4IWHNaIAhZ6uxBxeGHXQA24P3yYfBBhmgSBozESpwongWOBhggn/N1aKG8a1YY2oVAklgCgQUUwGJ8iXAgItrWUARbwpqIOWEal0ZoYJbzmWlZCWSlsAC6VkwZonNbMAAl5cpg+NiZwpnJ0Xylegmlc+tWY1mjnGnZnB4QukMA9UJRxGOf5r4ppqDjjmnfKilh2ejGiyJAgF1XNmYbC2GmhZ5AcJVgajcXecNqM9Rx8B6bingnlotviqdkB3YCg+rtOaapFsUhSrsq6axJ6sEwoZK7I/HWpCsr57FBxJ1w8LqV/81zbkoXK3LfVeNpic0KRQG4NHoIW/XEmZuaiN6tti62/moWbk18uhjqerWS6GFpe2YVotskVssWfBOAHACrZHoWcGQwQhlvmsdXBZ/F9YLMF2jzUuYBP4a7CLCnoEHrgkDSCDAARUILAGaVVqAwQHR8pZXomm9/ONhgjrbgc2lyYxmpIRK9uSNjrXs8gEbTrYyl2ryTJmsLCdKkWzFQl1lWlOXGmifal6p9VnbQfpyY2SZyXKVV7JmZkMrgIFSyrIeUJ2r7YKnXdivUg1kAgdQ8B7IzJjGsd9zKSdwyBL03WpwDGxwuOASEP5vriO2F3nLjQdIrpaRDxqcBdgIHGA74pKrZXiR2ZWuZt49m+o3pKMC3p4Av7SNxBa456777rz37jsVXRQh/PDEF2/88cgnr/zyzDfv/PMnhAAAIfkECQoAAAAsAAAAANwAEwAABf8gII5kaZ5oqq5s675wLM90bd94ru987//AoHBIBAgGhKRyyWw6n9CodEqtWq/YrHY6ELQEhYLDUPAMHGi0weEpbN7wI8cxTzsGj4R+n+DUxwaBeBt7hH1/gYIPhox+Y3Z3iwmGk36BkIN8egOIl3h8hBuOkAaZhQlna4BrpnyWa4mleZOFjrGKcXoFA2ReKwMJBgISDw6abwUPGggazc0bBqG0G8kI1tcIwZp51djW2nC03d7BjG8J49jl4cgP3t/RetLp1+vT6O7v5fKhAvnk0UKFogeP3zmCCIoZkDCABQFhChQYuKBHgkUJkxpA2MhxQYEDFhNcvPBAI8eNCx7/gMQYckPJkxsZPLhIM8FLmDJrYiRp8mTKkCwT8IQJwSPQkENhpgQpEunNkzlpWkwKdSbGihKocowqVSvKWQkIOBSgQOYFDBgQpI0oYMGEt3AzTLKm4BqGtnDjirxW95vbvG/nWlub8G9euRsiqqWLF/AEkRoiprX2wLDeDQgkW9PQGLDgyNc665WguK8C0XAnRY6oGPUEuRLsgk5g+a3cCxUqSBC7gsCBBXcVq6swwULx4hayvctGPK8FCwsSLE9A3Hje6NOrHzeOnW695sffRi/9HfDz7sIVSNB+XXrmugo0rHcM3X388o6jr44ceb51uNjF1xcC8zk3wXiS8aYC/wESaLABBs7ch0ECjr2WAGvLsLZBeHqVFl9kGxooV0T81TVhBo6NiOEyJ4p4IYnNRBQiYCN6x4wCG3ZAY2If8jXjYRcyk2FmG/5nXAY8wqhWAii+1YGOSGLoY4VRfqiAgikwmIeS1gjAgHkWYLQZf9m49V9gDWYWY5nmTYCRM2TS5pxxb8IZGV5nhplmhJyZadxzbrpnZ2d/6rnZgHIid5xIMDaDgJfbLdrgMkKW+Rygz1kEZz1mehabkBpgiQIByVikwGTqVfDkk2/Vxxqiqur4X3fksHccre8xlxerDLiHjQIVUAgXr77yFeyuOvYqXGbMrbrqBMqaFpFFzhL7qv9i1FX7ZLR0LUNdcc4e6Cus263KbV+inkAAHhJg0BeITR6WmHcaxhvXg/AJiKO9R77ILF1FwmVdAu6WBu+ZFua72mkZWMfqBElKu0G8rFZ5n4ATp5jkmvsOq+Nj7u63ZMMPv4bveyYy6fDH+C6brgnACHBABQUrkGirz2FwAHnM4Mmhzq9yijOrOi/MKabH6VwBiYwZdukEQAvILKTWXVq0ZvH5/CfUM7M29Zetthp1eht0eqkFYw8IKXKA6mzXfTeH7fZg9zW0AhgY0TwthUa6Ch9dBeIsbsFrYkRBfgTfiG0FhwMWnbsoq3cABUYOnu/ejU/A6uNeT8u4wMb1WnBCyJJTLjjnr8o3OeJrUcpc5oCiPqAEkz8tXuLkPeDL3Uhs4fvvwAcv/PDEU9FFEcgnr/zyzDfv/PPQRy/99NRXf0IIACH5BAkKAAAALAAAAADcABMAAAX/ICCOZGmeaKqubOu+cCzPdG3feK7vfO//wKBwSAQIBoSkcslsOp/QqHRKrVqv2Kx2OhC0BIWCw/AoDziOtCHt8BQ28PjmzK57Hom8fo42+P8DeAkbeYQcfX9+gYOFg4d1bIGEjQmPbICClI9/YwaLjHAJdJeKmZOViGtpn3qOqZineoeJgG8CeWUbBV4rAwkGAhIVGL97hGACGsrKCAgbBoTRhLvN1c3PepnU1s2/oZO6AtzdBoPf4eMI3tIJyOnF0YwFD+nY8e3z7+Xfefnj9uz8cVsXCh89axgk7BrAggAwBQsYIChwQILFixIeNIDAseOCBwcSXMy2sSPHjxJE/6a0eEGjSY4MQGK86PIlypUJEmYsaTKmyJ8JW/Ls6HMkzaEn8YwMWtPkx4pGd76E4DMPRqFTY860OGhogwYagBFoKEABA46DEGBAoEBB0AUT4sqdIFKBNbcC4M6dkEEk22oYFOTdG9fvWrtsBxM23MytYL17666t9phwXwlum2lIDHmuSA2IGyuOLOHv38qLMbdFjHruZbWgRXeOe1nC2BUEDiyAMMHZuwoTLAQX3nvDOAUW5Vogru434d4JnAsnPmFB9NBshQXfa9104+Rxl8e13rZxN+CEydtVsFkd+vDjE7C/q52wOvb4s7+faz025frbxefWbSoQIAEDEUCwgf9j7bUlwHN9ZVaegxDK1xYzFMJH24L5saXABhlYxiEzHoKoIV8LYqAMaw9aZqFmJUK4YHuNfRjiXhmk+NcyJgaIolvM8BhiBx3IleN8lH1IWAcRgkZgCgYiaBGJojGgHHFTgtagAFYSZhF7/qnTpY+faVlNAnqJN0EHWa6ozAZjBtgmmBokwMB01LW5jAZwbqfmlNips4B4eOqJgDJ2+imXRZpthuigeC6XZTWIxilXmRo8iYKBCwiWmWkJVEAkfB0w8KI1IvlIpKnOkVpqdB5+h96o8d3lFnijrgprjbfGRSt0lH0nAZG5vsprWxYRW6Suq4UWqrLEsspWg8Io6yv/q6EhK0Fw0GLbjKYn5CZYBYht1laPrnEY67kyrhYbuyceiR28Pso7bYwiXjihjWsWuWF5p/H765HmNoiur3RJsGKNG/jq748XMrwmjhwCfO6QD9v7LQsDxPTAMKsFpthyJCdkmgYiw0VdXF/Om9dyv7YMWGXTLYpZg5wNR11C78oW3p8HSGgul4qyrJppgllJHJZHn0Y0yUwDXCXUNquFZNLKyYXBAVZvxtAKYIQEsmPgDacr0tltO1y/DMwYpkgUpJfTasLGzd3cdCN3gN3UWRcY3epIEPevfq+3njBxq/kqBoGBduvea8f393zICS63ivRBTqgFpgaWZEIUULdcK+frIfAAL2AjscXqrLfu+uuwx05FF0XUbvvtuOeu++689+7778AHL/wJIQAAOwAAAAAAAAAAAA==\");\n\n"
  },
  {
    "path": "dirigible/shared/static/ace/cockpit.js",
    "content": "define(\"cockpit/index\",function(a,b,c){b.startup=function(b,c){a(\"pilot/index\"),a(\"cockpit/cli\").startup(b,c),a(\"cockpit/ui/settings\").startup(b,c),a(\"cockpit/ui/cli_view\").startup(b,c),a(\"cockpit/commands/basic\").startup(b,c)}}),define(\"cockpit/cli\",function(a,b,c){function r(a,b){q.call(this,a),b&&b.flags&&(this.flags=b.flags)}function q(a){this.env=a,this.commandAssignment=new o(p,this)}function o(a,b){this.param=a,this.requisition=b,this.setValue(a.defaultValue)}function n(a,b,c,d,e,f){this.emitter=a,this.setText(b),this.start=c,this.end=d,this.prefix=e,this.suffix=f}function m(a,b){this.status=a.status,this.message=a.message,b?(this.start=b.start,this.end=b.end):(this.start=0,this.end=0),this.predictions=a.predictions}function l(a,b,c,d,e){this.status=a,this.message=b;if(typeof c===\"number\")this.start=c,this.end=d,this.predictions=e;else{var f=c;this.start=f.start,this.end=f.end,this.predictions=f.predictions}}var d=a(\"pilot/console\"),e=a(\"pilot/lang\"),f=a(\"pilot/oop\"),g=a(\"pilot/event_emitter\").EventEmitter,h=a(\"pilot/types\"),i=a(\"pilot/types\").Status,j=a(\"pilot/types\").Conversion,k=a(\"pilot/canon\");b.startup=function(a,b){k.upgradeType(\"command\",p)},l.prototype={},l.sort=function(a,b){b!==undefined&&a.forEach(function(a){a.start===n.AT_CURSOR?a.distance=0:b<a.start?a.distance=a.start-b:b>a.end?a.distance=b-a.end:a.distance=0},this),a.sort(function(a,c){if(b!==undefined){var d=a.distance-c.distance;if(d!=0)return d}return c.status-a.status}),b!==undefined&&a.forEach(function(a){delete a.distance},this);return a},b.Hint=l,f.inherits(m,l),n.prototype={merge:function(a){if(a.emitter!=this.emitter)throw new Error(\"Can't merge Arguments from different EventEmitters\");return new n(this.emitter,this.text+this.suffix+a.prefix+a.text,this.start,a.end,this.prefix,a.suffix)},setText:function(a){if(a==null)throw new Error(\"Illegal text for Argument: \"+a);var b={argument:this,oldText:this.text,text:a};this.text=a,this.emitter._dispatchEvent(\"argumentChange\",b)},toString:function(){return this.prefix+this.text+this.suffix}},n.merge=function(a,b,c){b=b===undefined?0:b,c=c===undefined?a.length:c;var d;for(var e=b;e<c;e++){var f=a[e];d?d=d.merge(f):d=f}return d},n.AT_CURSOR=-1,o.prototype={param:undefined,conversion:undefined,value:undefined,arg:undefined,value:undefined,setValue:function(a){if(this.value!==a){if(a===undefined)this.value=this.param.defaultValue,this.conversion=this.param.getDefault?this.param.getDefault():this.param.type.getDefault(),this.arg=undefined;else{this.value=a,this.conversion=undefined;var b=a==null?\"\":this.param.type.stringify(a);this.arg&&this.arg.setText(b)}this.requisition._assignmentChanged(this)}},arg:undefined,setArgument:function(a){this.arg!==a&&(this.arg=a,this.conversion=this.param.type.parse(a.text),this.conversion.arg=a,this.value=this.conversion.value,this.requisition._assignmentChanged(this))},getHint:function(){if(this.param.getCustomHint&&this.value&&this.arg){var a=this.param.getCustomHint(this.value,this.arg);if(a)return a}var b=\"<strong>\"+this.param.name+\"</strong>: \";this.param.description&&(b+=this.param.description.trim(),b.charAt(b.length-1)!==\".\"&&(b+=\".\"),b.charAt(b.length-1)!==\" \"&&(b+=\" \"));var c=i.VALID,d=this.arg?this.arg.start:n.AT_CURSOR,e=this.arg?this.arg.end:n.AT_CURSOR,f;this.conversion&&(c=this.conversion.status,this.conversion.message&&(b+=this.conversion.message),f=this.conversion.predictions);var g=this.arg&&this.arg.text!==\"\",h=this.value!==undefined||g;this.param.defaultValue===undefined&&!h&&(c=i.INVALID,b+=\"<strong>Required<strong>\");return new l(c,b,d,e,f)},complete:function(){this.conversion&&this.conversion.predictions&&this.conversion.predictions.length>0&&this.setValue(this.conversion.predictions[0])},isPositionCaptured:function(a){if(!this.arg)return!1;if(this.arg.start===-1)return!1;if(a>this.arg.end)return!1;if(a===this.arg.end)return this.conversion.status!==i.VALID||this.conversion.predictions.length!==0;return!0},decrement:function(){var a=this.param.type.decrement(this.value);a!=null&&this.setValue(a)},increment:function(){var a=this.param.type.increment(this.value);a!=null&&this.setValue(a)},toString:function(){return this.arg?this.arg.toString():\"\"}},b.Assignment=o;var p={name:\"__command\",type:\"command\",description:\"The command to execute\",getCustomHint:function(a,b){var c=[];c.push(\"<strong><tt> &gt; \"),c.push(a.name),a.params&&a.params.length>0&&a.params.forEach(function(a){a.defaultValue===undefined?c.push(\" [\"+a.name+\"]\"):c.push(\" <em>[\"+a.name+\"]</em>\")},this),c.push(\"</tt></strong><br/>\"),c.push(a.description?a.description:\"(No description)\"),c.push(\"<br/>\"),a.params&&a.params.length>0&&(c.push(\"<ul>\"),a.params.forEach(function(a){c.push(\"<li>\"),c.push(\"<strong><tt>\"+a.name+\"</tt></strong>: \"),c.push(a.description?a.description:\"(No description)\"),a.defaultValue===undefined?c.push(\" <em>[Required]</em>\"):a.defaultValue===null?c.push(\" <em>[Optional]</em>\"):c.push(\" <em>[Default: \"+a.defaultValue+\"]</em>\"),c.push(\"</li>\")},this),c.push(\"</ul>\"));return new l(i.VALID,c.join(\"\"),b)}};q.prototype={commandAssignment:undefined,assignmentCount:undefined,_assignments:undefined,_hints:undefined,_assignmentChanged:function(a){a.param.name===\"__command\"&&(this._assignments={},a.value&&a.value.params.forEach(function(a){this._assignments[a.name]=new o(a,this)},this),this.assignmentCount=Object.keys(this._assignments).length,this._dispatchEvent(\"commandChange\",{command:a.value}))},getAssignment:function(a){var b=typeof a===\"string\"?a:Object.keys(this._assignments)[a];return this._assignments[b]},getParameterNames:function(){return Object.keys(this._assignments)},cloneAssignments:function(){return Object.keys(this._assignments).map(function(a){return this._assignments[a]},this)},_updateHints:function(){this.getAssignments(!0).forEach(function(a){this._hints.push(a.getHint())},this),l.sort(this._hints)},getWorstHint:function(){return this._hints[0]},getArgsObject:function(){var a={};this.getAssignments().forEach(function(b){a[b.param.name]=b.value},this);return a},getAssignments:function(a){var b=[];a===!0&&b.push(this.commandAssignment),Object.keys(this._assignments).forEach(function(a){b.push(this.getAssignment(a))},this);return b},setDefaultValues:function(){this.getAssignments().forEach(function(a){a.setValue(undefined)},this)},exec:function(){k.exec(this.commandAssignment.value,this.env,\"cli\",this.getArgsObject(),this.toCanonicalString())},toCanonicalString:function(){var a=[];a.push(this.commandAssignment.value.name),Object.keys(this._assignments).forEach(function(b){var c=this._assignments[b],d=c.param.type;c.value!==c.param.defaultValue&&(a.push(\" \"),a.push(d.stringify(c.value)))},this);return a.join(\"\")}},f.implement(q.prototype,g),b.Requisition=q,f.inherits(r,q),function(){r.prototype.update=function(a){this.input=a,this._hints=[];var b=this._tokenize(a.typed);this._split(b),this.commandAssignment.value&&this._assign(b),this._updateHints()},r.prototype.getInputStatusMarkup=function(){var a=this.toString().split(\"\").map(function(a){return i.VALID});this._hints.forEach(function(b){for(var c=b.start;c<=b.end;c++)b.status>a[c]&&(a[c]=b.status)},this);return a},r.prototype.toString=function(){return this.getAssignments(!0).map(function(a){return a.toString()},this).join(\"\")};var a=r.prototype._updateHints;r.prototype._updateHints=function(){a.call(this);var b=this.input.cursor;this._hints.forEach(function(a){var c=b.start>=a.start&&b.start<=a.end,d=b.end>=a.start&&b.end<=a.end,e=c||d;!e&&a.status===i.INCOMPLETE&&(a.status=i.INVALID)},this),l.sort(this._hints)},r.prototype.getHints=function(){return this._hints},r.prototype.getAssignmentAt=function(a){var b=this.getAssignments(!0);for(var c=0;c<b.length;c++){var d=b[c];if(!d.arg)return d;if(d.isPositionCaptured(a))return d}return d},r.prototype._tokenize=function(a){function g(a){return a.replace(/\\uF000/g,\" \").replace(/\\uF001/g,\"'\").replace(/\\uF002/g,'\"')}if(a==null||a.length===0)return[new n(this,\"\",0,0,\"\",\"\")];var b=1,c=2,d=3,e=4,f=b;a=a.replace(/\\\\\\\\/g,\"\\\\\").replace(/\\\\b/g,\"\\b\").replace(/\\\\f/g,\"\\f\").replace(/\\\\n/g,\"\\n\").replace(/\\\\r/g,\"\\r\").replace(/\\\\t/g,\"\\t\").replace(/\\\\v/g,\"\u000b\").replace(/\\\\n/g,\"\\n\").replace(/\\\\r/g,\"\\r\").replace(/\\\\ /g,\"\").replace(/\\\\'/g,\"\").replace(/\\\\\"/g,\"\");var h=0,i=0,j=\"\",k=[];while(!0){if(h>=a.length){if(f!==b){var l=g(a.substring(i,h));k.push(new n(this,l,i,h,j,\"\"))}else if(h!==i){var m=a.substring(i,h),o=k[k.length-1];o?o.suffix+=m:(o=new n(this,\"\",h,h,m,\"\"),k.push(o))}break}var p=a[h];switch(f){case b:p===\"'\"?(j=a.substring(i,h+1),f=d,i=h+1):p==='\"'?(j=a.substring(i,h+1),f=e,i=h+1):/ /.test(p)||(j=a.substring(i,h),f=c,i=h);break;case c:if(p===\" \"){var l=g(a.substring(i,h));k.push(new n(this,l,i,h,j,\"\")),f=b,i=h,j=\"\"}break;case d:if(p===\"'\"){var l=g(a.substring(i,h));k.push(new n(this,l,i-1,h+1,j,p)),f=b,i=h+1,j=\"\"}break;case e:if(p==='\"'){var l=g(a.substring(i,h));k.push(new n(this,l,i-1,h+1,j,p)),f=b,i=h+1,j=\"\"}}h++}return k},r.prototype._split=function(a){var b=1,c;while(b<=a.length){var c=n.merge(a,0,b);this.commandAssignment.setArgument(c);if(!this.commandAssignment.value)break;if(this.commandAssignment.value.exec){for(var d=0;d<b;d++)a.shift();break}b++}},r.prototype._assign=function(a){if(a.length===0)this.setDefaultValues();else{if(this.assignmentCount===0){this._hints.push(new l(i.INVALID,this.commandAssignment.value.name+\" does not take any parameters\",n.merge(a)));return}if(this.assignmentCount===1){var b=this.getAssignment(0);if(b.param.type.name===\"text\"){b.setArgument(n.merge(a));return}}var c=this.cloneAssignments(),d=this.getParameterNames(),f=[];c.forEach(function(b){var c=\"--\"+b.name,f=0;while(!0){var g=a[f];if(c!==g.text){f++;if(f>=a.length)break;continue}b.param.type.name===\"boolean\"?b.setValue(!0):f+1<a.length?this._hints.push(new l(i.INCOMPLETE,\"Missing value for: \"+c,a[f])):(a.splice(f+1,1),b.setArgument(a[f+1])),e.arrayRemove(d,b.name),a.splice(f,1)}},this),d.forEach(function(b){var c=this.getAssignment(b);if(a.length===0)c.setValue(undefined);else{var d=a[0];a.splice(0,1),c.setArgument(d)}},this);if(a.length>0){var g=n.merge(a);this._hints.push(new l(i.INVALID,\"Input '\"+g.text+\"' makes no sense.\",g))}}}}(),b.CliRequisition=r}),define(\"cockpit/ui/settings\",function(a,b,c){var d=a(\"pilot/types\"),e=a(\"pilot/types/basic\").SelectionType,f=new e({name:\"direction\",data:[\"above\",\"below\"]}),g={name:\"hintDirection\",description:\"Are hints shown above or below the command line?\",type:\"direction\",defaultValue:\"above\"},h={name:\"outputDirection\",description:\"Is the output window shown above or below the command line?\",type:\"direction\",defaultValue:\"above\"},i={name:\"outputHeight\",description:\"What height should the output panel be?\",type:\"number\",defaultValue:300};b.startup=function(a,b){d.registerType(f),a.env.settings.addSetting(g),a.env.settings.addSetting(h),a.env.settings.addSetting(i)},b.shutdown=function(a,b){d.unregisterType(f),a.env.settings.removeSetting(g),a.env.settings.removeSetting(h),a.env.settings.removeSetting(i)}}),define(\"cockpit/ui/cli_view\",function(a,b,c){function n(a,b){a.cliView=this,this.cli=a,this.doc=document,this.win=f.getParentWindow(this.doc),this.env=b,this.element=this.doc.getElementById(\"cockpitInput\");!this.element||(this.settings=b.settings,this.hintDirection=this.settings.getSetting(\"hintDirection\"),this.outputDirection=this.settings.getSetting(\"outputDirection\"),this.outputHeight=this.settings.getSetting(\"outputHeight\"),this.isUpdating=!1,this.createElements(),this.update())}var d=a(\"text!cockpit/ui/cli_view.css\"),e=a(\"pilot/event\"),f=a(\"pilot/dom\");f.importCssString(d);var e=a(\"pilot/event\"),g=a(\"pilot/keys\"),h=a(\"pilot/canon\"),i=a(\"pilot/types\").Status,j=a(\"cockpit/cli\").CliRequisition,k=a(\"cockpit/cli\").Hint,l=a(\"cockpit/ui/request_view\").RequestView,m=new k(i.VALID,\"\",0,0);b.startup=function(a,b){var c=new j(a.env),d=new n(c,a.env);a.env.cli=c},n.prototype={createElements:function(){function d(){f.removeCssClass(this.output,\"cptFocusPopup\"),f.removeCssClass(this.hinter,\"cptFocusPopup\")}var a=this.element;this.element.spellcheck=!1,this.output=this.doc.getElementById(\"cockpitOutput\"),this.popupOutput=this.output==null;if(!this.output){this.output=this.doc.createElement(\"div\"),this.output.id=\"cockpitOutput\",this.output.className=\"cptOutput\",a.parentNode.insertBefore(this.output,a.nextSibling);var b=function(){this.output.style.maxHeight=this.outputHeight.get()+\"px\"}.bind(this);this.outputHeight.addEventListener(\"change\",b),b()}this.completer=this.doc.createElement(\"div\"),this.completer.className=\"cptCompletion VALID\",this.completer.style.color=f.computedStyle(a,\"color\"),this.completer.style.fontSize=f.computedStyle(a,\"fontSize\"),this.completer.style.fontFamily=f.computedStyle(a,\"fontFamily\"),this.completer.style.fontWeight=f.computedStyle(a,\"fontWeight\"),this.completer.style.fontStyle=f.computedStyle(a,\"fontStyle\"),a.parentNode.insertBefore(this.completer,a.nextSibling),this.completer.style.backgroundColor=a.style.backgroundColor,a.style.backgroundColor=\"transparent\",this.hinter=this.doc.createElement(\"div\"),this.hinter.className=\"cptHints\",a.parentNode.insertBefore(this.hinter,a.nextSibling);var c=this.resizer.bind(this);e.addListener(this.win,\"resize\",c),this.hintDirection.addEventListener(\"change\",c),this.outputDirection.addEventListener(\"change\",c),c(),h.addEventListener(\"output\",function(a){new l(a.request,this)}.bind(this)),e.addCommandKeyListener(a,this.onCommandKey.bind(this)),e.addListener(a,\"keyup\",this.onKeyUp.bind(this)),e.addListener(a,\"mouseup\",function(a){this.isUpdating=!0,this.update(),this.isUpdating=!1}.bind(this)),this.cli.addEventListener(\"argumentChange\",this.onArgChange.bind(this)),e.addListener(a,\"focus\",function(){f.addCssClass(this.output,\"cptFocusPopup\"),f.addCssClass(this.hinter,\"cptFocusPopup\")}.bind(this)),e.addListener(a,\"blur\",d.bind(this)),d.call(this)},scrollOutputToBottom:function(){var a=Math.max(this.output.scrollHeight,this.output.clientHeight);this.output.scrollTop=a-this.output.clientHeight},resizer:function(){var a=this.element.getClientRects()[0];this.completer.style.top=a.top+\"px\";var b=a.bottom-a.top;this.completer.style.height=b+\"px\",this.completer.style.lineHeight=b+\"px\",this.completer.style.left=a.left+\"px\";var c=a.right-a.left;this.completer.style.width=c+\"px\",this.hintDirection.get()===\"below\"?(this.hinter.style.top=a.bottom+\"px\",this.hinter.style.bottom=\"auto\"):(this.hinter.style.top=\"auto\",this.hinter.style.bottom=this.doc.documentElement.clientHeight-a.top+\"px\"),this.hinter.style.left=a.left+30+\"px\",this.hinter.style.maxWidth=c-110+\"px\",this.popupOutput&&(this.outputDirection.get()===\"below\"?(this.output.style.top=a.bottom+\"px\",this.output.style.bottom=\"auto\"):(this.output.style.top=\"auto\",this.output.style.bottom=this.doc.documentElement.clientHeight-a.top+\"px\"),this.output.style.left=a.left+\"px\",this.output.style.width=c-80+\"px\")},onCommandKey:function(a,b,c){var d;if(c===g.TAB||c===g.UP||c===g.DOWN)d=!0;else if(b!=0||c!=0)d=h.execKeyCommand(this.env,\"cli\",b,c);d&&e.stopEvent(a)},onKeyUp:function(a){var b;if(a.keyCode===g.RETURN){var c=this.cli.getWorstHint();c.status===i.VALID?(this.cli.exec(),this.element.value=\"\"):(f.setSelectionStart(this.element,c.start),f.setSelectionEnd(this.element,c.end))}this.update();var d=this.cli.getAssignmentAt(f.getSelectionStart(this.element));d&&(a.keyCode===g.TAB&&(d.complete(),this.update()),a.keyCode===g.UP&&(d.increment(),this.update()),a.keyCode===g.DOWN&&(d.decrement(),this.update()));return b},update:function(){this.isUpdating=!0;var a={typed:this.element.value,cursor:{start:f.getSelectionStart(this.element),end:f.getSelectionEnd(this.element.selectionEnd)}};this.cli.update(a);var b=this.cli.getAssignmentAt(a.cursor.start).getHint();f.removeCssClass(this.completer,i.VALID.toString()),f.removeCssClass(this.completer,i.INCOMPLETE.toString()),f.removeCssClass(this.completer,i.INVALID.toString());var c='<span class=\"cptPrompt\">&gt;</span> ';if(this.element.value.length>0){var d=this.cli.getInputStatusMarkup();c+=this.markupStatusScore(d)}if(this.element.value.length>0&&b.predictions&&b.predictions.length>0){var e=b.predictions[0];c+=\" &nbsp;&#x21E5; \"+(e.name?e.name:e)}this.completer.innerHTML=c,f.addCssClass(this.completer,this.cli.getWorstHint().status.toString());var g=\"\";this.element.value.length!==0&&(g+=b.message,b.predictions&&b.predictions.length>0&&(g+=\": [ \",b.predictions.forEach(function(a){g+=a.name?a.name:a,g+=\" | \"},this),g=g.replace(/\\| $/,\"]\"))),this.hinter.innerHTML=g,g.length===0?f.addCssClass(this.hinter,\"cptNoPopup\"):f.removeCssClass(this.hinter,\"cptNoPopup\"),this.isUpdating=!1},markupStatusScore:function(a){var b=\"\",c=0,d=-1;while(!0){d!==a[c]&&(b+=\"<span class=\"+a[c].toString()+\">\",d=a[c]),b+=this.element.value[c],c++;if(c===this.element.value.length){b+=\"</span>\";break}d!==a[c]&&(b+=\"</span>\")}return b},onArgChange:function(a){if(!this.isUpdating){var b=this.element.value.substring(0,a.argument.start),c=this.element.value.substring(a.argument.end),d=typeof a.text===\"string\"?a.text:a.text.name;this.element.value=b+d+c;var e=(b+d).length;this.element.selectionStart=e,this.element.selectionEnd=e}}},b.CliView=n}),define(\"cockpit/ui/request_view\",function(a,b,c){function l(a,b){this.request=a,this.cliView=b,this.imageUrl=k,this.rowin=null,this.rowout=null,this.output=null,this.hide=null,this.show=null,this.duration=null,this.throb=null,(new g).processNode(j.cloneNode(!0),this),this.cliView.output.appendChild(this.rowin),this.cliView.output.appendChild(this.rowout),this.request.addEventListener(\"output\",this.onRequestChange.bind(this))}function k(b){var d;try{d=a(\"text!cockpit/ui/\"+b)}catch(e){}if(d)return d;var f=c.id.split(\"/\").pop()+\".js\",g;if(c.uri.substr(-f.length)!==f){console.error(\"Can't work out path from module.uri/module.id\");return b}if(c.uri){var h=c.uri.length-f.length-1;return c.uri.substr(0,h)+\"/\"+b}return f+b}var d=a(\"pilot/dom\"),e=a(\"pilot/event\"),f=a(\"text!cockpit/ui/request_view.html\"),g=a(\"pilot/domtemplate\").Templater,h=a(\"text!cockpit/ui/request_view.css\");d.importCssString(h);var i=document.createElement(\"div\");i.innerHTML=f;var j=i.querySelector(\".cptRow\");l.prototype={copyToInput:function(){this.cliView.element.value=this.request.typed},executeRequest:function(a){this.cliView.cli.update({typed:this.request.typed,cursor:{start:0,end:0}}),this.cliView.cli.exec()},hideOutput:function(a){this.output.style.display=\"none\",d.addCssClass(this.hide,\"cmd_hidden\"),d.removeCssClass(this.show,\"cmd_hidden\"),e.stopPropagation(a)},showOutput:function(a){this.output.style.display=\"block\",d.removeCssClass(this.hide,\"cmd_hidden\"),d.addCssClass(this.show,\"cmd_hidden\"),e.stopPropagation(a)},remove:function(a){this.cliView.output.removeChild(this.rowin),this.cliView.output.removeChild(this.rowout),e.stopPropagation(a)},onRequestChange:function(a){this.duration.innerHTML=this.request.duration?\"completed in \"+this.request.duration/1e3+\" sec \":\"\",this.output.innerHTML=\"\",this.request.outputs.forEach(function(a){var b;typeof a==\"string\"?(b=document.createElement(\"p\"),b.innerHTML=a):b=a,this.output.appendChild(b)},this),this.cliView.scrollOutputToBottom(),d.setCssClass(this.output,\"cmd_error\",this.request.error),this.throb.style.display=this.request.completed?\"none\":\"block\"}},b.RequestView=l}),define(\"pilot/domtemplate\",function(require,exports,module){function Templater(){this.scope=[]}Templater.prototype.processNode=function(a,b){typeof a===\"string\"&&(a=document.getElementById(a));if(b===null||b===undefined)b={};this.scope.push(a.nodeName+(a.id?\"#\"+a.id:\"\"));try{if(a.attributes&&a.attributes.length){if(a.hasAttribute(\"foreach\")){this.processForEach(a,b);return}if(a.hasAttribute(\"if\")&&!this.processIf(a,b))return;b.__element=a;var c=Array.prototype.slice.call(a.attributes);for(var d=0;d<c.length;d++){var e=c[d].value,f=c[d].name;this.scope.push(f);try{if(f===\"save\")e=this.stripBraces(e),this.property(e,b,a),a.removeAttribute(\"save\");else if(f.substring(0,2)===\"on\"){e=this.stripBraces(e);var g=this.property(e,b);typeof g!==\"function\"&&this.handleError(\"Expected \"+e+\" to resolve to a function, but got \"+typeof g),a.removeAttribute(f);var h=a.hasAttribute(\"capture\"+f.substring(2));a.addEventListener(f.substring(2),g,h),h&&a.removeAttribute(\"capture\"+f.substring(2))}else{var i=this,j=e.replace(/\\$\\{[^}]*\\}/g,function(a){return i.envEval(a.slice(2,-1),b,e)});f.charAt(0)===\"_\"?(a.removeAttribute(f),a.setAttribute(f.substring(1),j)):e!==j&&(c[d].value=j)}}finally{this.scope.pop()}}}var k=Array.prototype.slice.call(a.childNodes);for(var l=0;l<k.length;l++)this.processNode(k[l],b);a.nodeType===Node.TEXT_NODE&&this.processTextNode(a,b)}finally{this.scope.pop()}},Templater.prototype.processIf=function(a,b){this.scope.push(\"if\");try{var c=a.getAttribute(\"if\"),d=this.stripBraces(c),e=!0;try{var f=this.envEval(d,b,c);e=!!f}catch(g){this.handleError(\"Error with '\"+d+\"'\",g),e=!1}e||a.parentNode.removeChild(a),a.removeAttribute(\"if\");return e}finally{this.scope.pop()}},Templater.prototype.processForEach=function(a,b){this.scope.push(\"foreach\");try{var c=a.getAttribute(\"foreach\"),d=c,e=\"param\";if(d.charAt(0)===\"$\")d=this.stripBraces(d);else{var f=d.split(\" in \");e=f[0].trim(),d=this.stripBraces(f[1].trim())}a.removeAttribute(\"foreach\");try{var g=this,h=function(a,c,d){d.parentNode.insertBefore(c,d),b[e]=a,g.processNode(c,b),delete b[e]},i=function(b,c){g.scope.push(b);try{if(a.nodeName===\"LOOP\")for(var d=0;d<a.childNodes.length;d++){var e=a.childNodes[d].cloneNode(!0);h(c,e,a)}else{var e=a.cloneNode(!0);e.removeAttribute(\"foreach\"),h(c,e,a)}}finally{g.scope.pop()}},j=this.envEval(d,b,c);if(Array.isArray(j))j.forEach(function(a,b){i(\"\"+b,a)},this);else for(var k in j)j.hasOwnProperty(k)&&i(k,k);a.parentNode.removeChild(a)}catch(l){this.handleError(\"Error with '\"+d+\"'\",l)}}finally{this.scope.pop()}},Templater.prototype.processTextNode=function(a,b){var c=a.data;c=c.replace(/\\$\\{([^}]*)\\}/g,\"$$$1\");var d=c.split(/\\uF001|\\uF002/);d.length>1&&(d.forEach(function(c){c!==null&&c!==undefined&&c!==\"\"&&(c.charAt(0)===\"$\"&&(c=this.envEval(c.slice(1),b,a.data)),c===null&&(c=\"null\"),c===undefined&&(c=\"undefined\"),typeof c.cloneNode!==\"function\"&&(c=a.ownerDocument.createTextNode(c.toString())),a.parentNode.insertBefore(c,a))},this),a.parentNode.removeChild(a))},Templater.prototype.stripBraces=function(a){if(!a.match(/\\$\\{.*\\}/g)){this.handleError(\"Expected \"+a+\" to match ${...}\");return a}return a.slice(2,-1)},Templater.prototype.property=function(a,b,c){this.scope.push(a);try{typeof a===\"string\"&&(a=a.split(\".\"));var d=b[a[0]];if(a.length===1){c!==undefined&&(b[a[0]]=c);if(typeof d===\"function\")return function(){return d.apply(b,arguments)};return d}if(!d){this.handleError(\"Can't find path=\"+a);return null}return this.property(a.slice(1),d,c)}finally{this.scope.pop()}},Templater.prototype.envEval=function(script,env,context){with(env)try{this.scope.push(context);return eval(script)}catch(ex){this.handleError(\"Template error evaluating '\"+script+\"'\",ex);return script}finally{this.scope.pop()}},Templater.prototype.handleError=function(a,b){this.logError(a),this.logError(\"In: \"+this.scope.join(\" > \")),b&&this.logError(b)},Templater.prototype.logError=function(a){window.console&&window.console.log&&console.log(a)},exports.Templater=Templater}),define(\"cockpit/commands/basic\",function(a,b,c){var d=a(\"pilot/canon\"),e={name:\"sh\",description:\"Execute a system command (requires server support)\",params:[{name:\"command\",type:\"text\",description:\"The string to send to the os shell.\"}],exec:function(a,b,c){var d=new XMLHttpRequest;d.open(\"GET\",\"/exec?args=\"+b.command,!0),d.onreadystatechange=function(a){d.readyState==4&&d.status==200&&c.done(\"<pre>\"+d.responseText+\"</pre>\")},d.send(null)}},d=a(\"pilot/canon\");b.startup=function(a,b){d.addCommand(e)},b.shutdown=function(a,b){d.removeCommand(e)}}),define(\"text!cockpit/ui/cli_view.css\",\"#cockpitInput { padding-left: 16px; }.cptOutput { overflow: auto; position: absolute; z-index: 999; display: none; }.cptCompletion { padding: 0; position: absolute; z-index: -1000; }.cptCompletion.VALID { background: #FFF; }.cptCompletion.INCOMPLETE { background: #DDD; }.cptCompletion.INVALID { background: #DDD; }.cptCompletion span { color: #FFF; }.cptCompletion span.INCOMPLETE { color: #DDD; border-bottom: 2px dotted #F80; }.cptCompletion span.INVALID { color: #DDD; border-bottom: 2px dotted #F00; }span.cptPrompt { color: #66F; font-weight: bold; }.cptHints {  color: #000;  position: absolute;  border: 1px solid rgba(230, 230, 230, 0.8);  background: rgba(250, 250, 250, 0.8);  -moz-border-radius-topleft: 10px;  -moz-border-radius-topright: 10px;  border-top-left-radius: 10px; border-top-right-radius: 10px;  z-index: 1000;  padding: 8px;  display: none;}.cptFocusPopup { display: block; }.cptFocusPopup.cptNoPopup { display: none; }.cptHints ul { margin: 0; padding: 0 15px; }.cptGt { font-weight: bold; font-size: 120%; }\"),define(\"text!cockpit/ui/request_view.css\",\".cptRowIn {  display: box; display: -moz-box; display: -webkit-box;  box-orient: horizontal; -moz-box-orient: horizontal; -webkit-box-orient: horizontal;  box-align: center; -moz-box-align: center; -webkit-box-align: center;  color: #333;  background-color: #EEE;  width: 100%;  font-family: consolas, courier, monospace;}.cptRowIn > * { padding-left: 2px; padding-right: 2px; }.cptRowIn > img { cursor: pointer; }.cptHover { display: none; }.cptRowIn:hover > .cptHover { display: block; }.cptRowIn:hover > .cptHover.cptHidden { display: none; }.cptOutTyped {  box-flex: 1; -moz-box-flex: 1; -webkit-box-flex: 1;  font-weight: bold; color: #000; font-size: 120%;}.cptRowOutput { padding-left: 10px; line-height: 1.2em; }.cptRowOutput strong,.cptRowOutput b,.cptRowOutput th,.cptRowOutput h1,.cptRowOutput h2,.cptRowOutput h3 { color: #000; }.cptRowOutput a { font-weight: bold; color: #666; text-decoration: none; }.cptRowOutput a: hover { text-decoration: underline; cursor: pointer; }.cptRowOutput input[type=password],.cptRowOutput input[type=text],.cptRowOutput textarea {  color: #000; font-size: 120%;  background: transparent; padding: 3px;  border-radius: 5px; -moz-border-radius: 5px; -webkit-border-radius: 5px;}.cptRowOutput table,.cptRowOutput td,.cptRowOutput th { border: 0; padding: 0 2px; }.cptRowOutput .right { text-align: right; }\"),define(\"text!cockpit/ui/request_view.html\",'<div class=cptRow>  <!-- The div for the input (i.e. what was typed) -->  <div class=\"cptRowIn\" save=\"${rowin}\"      onclick=\"${copyToInput}\"      ondblclick=\"${executeRequest}\">    <!-- What the user actually typed -->    <div class=\"cptGt\">&gt; </div>    <div class=\"cptOutTyped\">${request.typed}</div>    <!-- The extra details that appear on hover -->    <div class=cptHover save=\"${duration}\"></div>    <img class=cptHover onclick=\"${hideOutput}\" save=\"${hide}\"        alt=\"Hide command output\" _src=\"${imageUrl(\\'images/minus.png\\')}\"/>    <img class=\"cptHover cptHidden\" onclick=\"${showOutput}\" save=\"${show}\"        alt=\"Show command output\" _src=\"${imageUrl(\\'images/plus.png\\')}\"/>    <img class=cptHover onclick=\"${remove}\"        alt=\"Remove this command from the history\"        _src=\"${imageUrl(\\'images/closer.png\\')}\"/>  </div>  <!-- The div for the command output -->  <div class=\"cptRowOut\" save=\"${rowout}\">    <div class=\"cptRowOutput\" save=\"${output}\"></div>    <img _src=\"${imageUrl(\\'images/throbber.gif\\')}\" save=\"${throb}\"/>  </div></div>'),define(\"text!cockpit/ui/images/closer.png\",\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAj9JREFUeNp0ks+LUlEUx7/vV1o8Z8wUx3IEHcQmiBiQlomjRNCiZpEuEqF/oEUwq/6EhvoHggmRcJUQBM1CRJAW0aLIaGQimZJxJsWxyV/P9/R1zzWlFl04vPvOPZ9z7rnnK5imidmKRCIq+zxgdoPZ1T/ut8xeM3tcKpW6s1hhBkaj0Qj7bDebTX+324WmadxvsVigqipcLleN/d4rFoulORiLxTZY8ItOp8MBCpYkiYPj8Xjus9vtlORWoVB4KcTjcQc732dLpSRXvCZaAws6Q4WDdqsO52kNH+oCRFGEz+f7ydwBKRgMPmTXi49GI1x2D/DsznesB06ws2eDbI7w9HYN6bVjvGss4KAjwDAMq81mM2SW5Wa/3weBbz42UL9uYnVpiO2Nr9ANHSGXib2Wgm9tCYIggGKJEVkvlwgi5/FQRmTLxO6hgJVzI1x0T/fJrBtHJxPeL6tI/fsZLA6ot8lkQi8HRVbw94gkWYI5MaHrOjcCGSNRxZosy9y5cErDzn0Dqx7gcwO8WtBp4PndI35GMYqiUMUvBL5yOBz8yRfFNpbPmqgcCFh/IuHa1nR/YXGM8+oUpFhihEQiwcdRLpfVRqOBtWXWq34Gra6AXq8Hp2piZcmKT4cKnE4nwuHwdByVSmWQz+d32WCTlHG/qaHHREN9kgi0sYQfv0R4PB4EAgESQDKXy72fSy6VSnHJVatVf71eR7vd5n66mtfrRSgU4pLLZrOlf7RKK51Ok8g3/yPyR5lMZi7y3wIMAME4EigHWgKnAAAAAElFTkSuQmCC\"),define(\"text!cockpit/ui/images/dot_clear.gif\",\"data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAEBMgA7\"),define(\"text!cockpit/ui/images/minus.png\",\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAAZiS0dEANIA0gDS7KbF4AAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9kFGw4xMrIJw5EAAAHcSURBVCjPhZIxSxtxGMZ/976XhJA/RA5EAyJcFksnp64hjUPBoXRyCYLQTyD0UxScu0nFwalCQSgFCVk7dXAwUAiBDA2RO4W7yN1x9+9gcyhU+pteHt4H3pfncay1LOl0OgY4BN4Ar/7KP4BvwNFwOIyWu87S2O12O8DxfD73oygiSRIAarUaxhhWV1fHwMFgMBiWxl6v9y6Koi+3t7ckSUKtVkNVAcjzvNRWVlYwxry9vLz86uzs7HjAZDKZGGstjUaDfxHHMSLC5ubmHdB2VfVwNpuZ5clxHPMcRVFwc3PTXFtbO3RFZHexWJCmabnweAaoVqvlv4vFAhHZdVX1ZZqmOI5DURR8fz/lxbp9Yrz+7bD72SfPcwBU1XdF5N5aWy2KgqIoeBzPEnWVLMseYnAcRERdVR27rrsdxzGqyutP6898+GBsNBqo6i9XVS88z9sOggAR4X94noeqXoiIHPm+H9XrdYIgIAxDwjAkTVPCMESzBy3LMprNJr7v34nIkV5dXd2fn59fG2P2siwjSRIqlQrWWlSVJFcqlQqtVot2u40xZu/s7OxnWbl+v98BjkejkT+dTgmCoDxtY2ODra2tMXBweno6fNJVgP39fQN8eKbkH09OTsqS/wHFRdHPfTSfjwAAAABJRU5ErkJggg==\"),define(\"text!cockpit/ui/images/pinaction.png\",\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAC7mlDQ1BJQ0MgUHJvZmlsZQAAeAGFVM9rE0EU/jZuqdAiCFprDrJ4kCJJWatoRdQ2/RFiawzbH7ZFkGQzSdZuNuvuJrWliOTi0SreRe2hB/+AHnrwZC9KhVpFKN6rKGKhFy3xzW5MtqXqwM5+8943731vdt8ADXLSNPWABOQNx1KiEWlsfEJq/IgAjqIJQTQlVdvsTiQGQYNz+Xvn2HoPgVtWw3v7d7J3rZrStpoHhP1A4Eea2Sqw7xdxClkSAog836Epx3QI3+PY8uyPOU55eMG1Dys9xFkifEA1Lc5/TbhTzSXTQINIOJT1cVI+nNeLlNcdB2luZsbIEL1PkKa7zO6rYqGcTvYOkL2d9H5Os94+wiHCCxmtP0a4jZ71jNU/4mHhpObEhj0cGDX0+GAVtxqp+DXCFF8QTSeiVHHZLg3xmK79VvJKgnCQOMpkYYBzWkhP10xu+LqHBX0m1xOv4ndWUeF5jxNn3tTd70XaAq8wDh0MGgyaDUhQEEUEYZiwUECGPBoxNLJyPyOrBhuTezJ1JGq7dGJEsUF7Ntw9t1Gk3Tz+KCJxlEO1CJL8Qf4qr8lP5Xn5y1yw2Fb3lK2bmrry4DvF5Zm5Gh7X08jjc01efJXUdpNXR5aseXq8muwaP+xXlzHmgjWPxHOw+/EtX5XMlymMFMXjVfPqS4R1WjE3359sfzs94i7PLrXWc62JizdWm5dn/WpI++6qvJPmVflPXvXx/GfNxGPiKTEmdornIYmXxS7xkthLqwviYG3HCJ2VhinSbZH6JNVgYJq89S9dP1t4vUZ/DPVRlBnM0lSJ93/CKmQ0nbkOb/qP28f8F+T3iuefKAIvbODImbptU3HvEKFlpW5zrgIXv9F98LZua6N+OPwEWDyrFq1SNZ8gvAEcdod6HugpmNOWls05Uocsn5O66cpiUsxQ20NSUtcl12VLFrOZVWLpdtiZ0x1uHKE5QvfEp0plk/qv8RGw/bBS+fmsUtl+ThrWgZf6b8C8/UXAeIuJAAAACXBIWXMAAAsTAAALEwEAmpwYAAAClklEQVQ4EX1TXUhUQRQ+Z3Zmd+9uN1q2P3UpZaEwcikKekkqLKggKHJ96MHe9DmLkCDa9U198Id8kErICmIlRAN96UdE6QdBW/tBA5Uic7E0zN297L17p5mb1zYjD3eYc+d83zlnON8g5xzWNUSEdUBkHTJasRWySPP7fw3hfwkk2GoNsc0vOaJRHo1GV/GiMctkTIJRFlpZli8opK+htmf83gXeG63oteOtra0u25e7TYJIJELb26vYCACTgUe1lXV86BTn745l+MsyHqs53S/Aq4VEUa9Y6ko14eYY4u3AyM3HYwdKU35DZyblGR2+qq6W0X2Nnh07xynnVYpHORx/E1/GvvqaAZUayjMjdM2f/Lgr5E+fV93zR4u3zKCLughsZqKwAzAxaz6dPY6JgjLUF+eSP5OpjmAw2E8DvldHSvJMKPg08aRor1tc4BuALu6mOwGWdQC3mKIqRsC8mKd8wYfD78/earzSYzdMDW9QgKb0Is8CBY1mQXOiaXAHEpMDE5XTJqIq4EiyxUqKlpfkF0pyV1OTAoFAhmTmyCCoDsZNZvIkUjELQpipo0sQqYZAswZHwsEEE10M0pq2SSZY9HqNcDicJcNTpBvQJz40UbSOTh1B8bDpuY0w9Hb3kkn9lPAlBLfhfD39XTtX/blFJqiqrjbkTi63Hbofj2uL4GMsmzFgbDJ/vmMgv/lB4syJ0oXO7d3j++vio6GFsYmD6cHJreWc3/jRVVHhsOYvM8iZ36mtjPDBk/xDZE8CoHlbrlAssbTxDdDJvdb536L7I6S7Vy++6Gi4Xi9BsUthJRaLOYSPz4XALKI4j4iObd/e5UtDKUjZzYyYRyGAJv01Zj8kC5cbs5WY83hQnv0DzCXl+r8APElkq0RU6oMAAAAASUVORK5CYII=\"),define(\"text!cockpit/ui/images/pinin.png\",\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAC7mlDQ1BJQ0MgUHJvZmlsZQAAeAGFVM9rE0EU/jZuqdAiCFprDrJ4kCJJWatoRdQ2/RFiawzbH7ZFkGQzSdZuNuvuJrWliOTi0SreRe2hB/+AHnrwZC9KhVpFKN6rKGKhFy3xzW5MtqXqwM5+8943731vdt8ADXLSNPWABOQNx1KiEWlsfEJq/IgAjqIJQTQlVdvsTiQGQYNz+Xvn2HoPgVtWw3v7d7J3rZrStpoHhP1A4Eea2Sqw7xdxClkSAog836Epx3QI3+PY8uyPOU55eMG1Dys9xFkifEA1Lc5/TbhTzSXTQINIOJT1cVI+nNeLlNcdB2luZsbIEL1PkKa7zO6rYqGcTvYOkL2d9H5Os94+wiHCCxmtP0a4jZ71jNU/4mHhpObEhj0cGDX0+GAVtxqp+DXCFF8QTSeiVHHZLg3xmK79VvJKgnCQOMpkYYBzWkhP10xu+LqHBX0m1xOv4ndWUeF5jxNn3tTd70XaAq8wDh0MGgyaDUhQEEUEYZiwUECGPBoxNLJyPyOrBhuTezJ1JGq7dGJEsUF7Ntw9t1Gk3Tz+KCJxlEO1CJL8Qf4qr8lP5Xn5y1yw2Fb3lK2bmrry4DvF5Zm5Gh7X08jjc01efJXUdpNXR5aseXq8muwaP+xXlzHmgjWPxHOw+/EtX5XMlymMFMXjVfPqS4R1WjE3359sfzs94i7PLrXWc62JizdWm5dn/WpI++6qvJPmVflPXvXx/GfNxGPiKTEmdornIYmXxS7xkthLqwviYG3HCJ2VhinSbZH6JNVgYJq89S9dP1t4vUZ/DPVRlBnM0lSJ93/CKmQ0nbkOb/qP28f8F+T3iuefKAIvbODImbptU3HvEKFlpW5zrgIXv9F98LZua6N+OPwEWDyrFq1SNZ8gvAEcdod6HugpmNOWls05Uocsn5O66cpiUsxQ20NSUtcl12VLFrOZVWLpdtiZ0x1uHKE5QvfEp0plk/qv8RGw/bBS+fmsUtl+ThrWgZf6b8C8/UXAeIuJAAAACXBIWXMAAAsTAAALEwEAmpwYAAABZ0lEQVQ4Ea2TPUsDQRCGZ89Eo4FACkULEQs1CH4Uamfjn7GxEYJFIFXgChFsbPwzNnZioREkaiHBQtEiEEiMRm/dZ8OEGAxR4sBxx877Pju7M2estTJIxLrNuVwuMxQEx0ZkzcFHyRtjXt02559RtB2GYanTYzoryOfz+6l4Nbszf2niwffKmpGRo9sVW22mDgqFwp5C2gDMm+P32a3JB1N+n5JifUGeP9JeNxGryPLYjcwMP8rJ07Q9fZltQzyAstOJ2vVu5sKc1ZZkRBrOcKeb+HexPidvkpCN5JUcllZtpZFc5DgBWc5M2eysZuMuofMBSA4NWjx4PUCsXefMlI0QY3ewRg4NWi4ZTQsgrjYXema+e4VqtEMK6KXvu+4B9Bklt90vVKMeD2BI6DOt4rZ/Gk7WyKFBi4fNPIAJY0joM61SCCZ9tI1o0OIB8D+DBIkYaJRbCBH9mZgNt+bb++ufSSF/eX8BYcDeAzuQJVUAAAAASUVORK5CYII=\"),define(\"text!cockpit/ui/images/pinout.png\",\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAC7mlDQ1BJQ0MgUHJvZmlsZQAAeAGFVM9rE0EU/jZuqdAiCFprDrJ4kCJJWatoRdQ2/RFiawzbH7ZFkGQzSdZuNuvuJrWliOTi0SreRe2hB/+AHnrwZC9KhVpFKN6rKGKhFy3xzW5MtqXqwM5+8943731vdt8ADXLSNPWABOQNx1KiEWlsfEJq/IgAjqIJQTQlVdvsTiQGQYNz+Xvn2HoPgVtWw3v7d7J3rZrStpoHhP1A4Eea2Sqw7xdxClkSAog836Epx3QI3+PY8uyPOU55eMG1Dys9xFkifEA1Lc5/TbhTzSXTQINIOJT1cVI+nNeLlNcdB2luZsbIEL1PkKa7zO6rYqGcTvYOkL2d9H5Os94+wiHCCxmtP0a4jZ71jNU/4mHhpObEhj0cGDX0+GAVtxqp+DXCFF8QTSeiVHHZLg3xmK79VvJKgnCQOMpkYYBzWkhP10xu+LqHBX0m1xOv4ndWUeF5jxNn3tTd70XaAq8wDh0MGgyaDUhQEEUEYZiwUECGPBoxNLJyPyOrBhuTezJ1JGq7dGJEsUF7Ntw9t1Gk3Tz+KCJxlEO1CJL8Qf4qr8lP5Xn5y1yw2Fb3lK2bmrry4DvF5Zm5Gh7X08jjc01efJXUdpNXR5aseXq8muwaP+xXlzHmgjWPxHOw+/EtX5XMlymMFMXjVfPqS4R1WjE3359sfzs94i7PLrXWc62JizdWm5dn/WpI++6qvJPmVflPXvXx/GfNxGPiKTEmdornIYmXxS7xkthLqwviYG3HCJ2VhinSbZH6JNVgYJq89S9dP1t4vUZ/DPVRlBnM0lSJ93/CKmQ0nbkOb/qP28f8F+T3iuefKAIvbODImbptU3HvEKFlpW5zrgIXv9F98LZua6N+OPwEWDyrFq1SNZ8gvAEcdod6HugpmNOWls05Uocsn5O66cpiUsxQ20NSUtcl12VLFrOZVWLpdtiZ0x1uHKE5QvfEp0plk/qv8RGw/bBS+fmsUtl+ThrWgZf6b8C8/UXAeIuJAAAACXBIWXMAAAsTAAALEwEAmpwYAAACyUlEQVQ4EW1TXUgUURQ+Z3ZmnVV3QV2xJbVSEIowQbAfLQx8McLoYX2qjB58MRSkP3vZppceYhGxgrZaIughlYpE7CHFWiiKyj9II0qxWmwlNh1Xtp2f27mz7GDlZX7uuXO+73zfuXeQMQYIgAyALppgyBtse32stsw86txkHhATn+FbfPfzxnPB+vR3RMJYuTwW6bbB4a6WS5O3Yu2VlXIesDiAamiQNKVlVXfx5I0GJ7DY7p0/+erU4dgeMJIA31WNxZmAgibOreXDqF55sY4SFUURqbi+nkjgwTyAbHhLX8yOLsSM2QRA3JRAAgd4RGPbVhkKEp8qeJ7PFyW3fw++YHtC7CkaD0amqyqihSwlMQQ0wa07IjPVI/vbexreIUrVaQV2D4RMQ/o7m12Mdfx4H3PfB9FNzTR1U2cO0Bi45aV6xNvFBNaoIAfbSiwLlqi9/hR/R3Nrhua+Oqi9TEKiB02C7YXz+Pba4MTDrpbLiMAxNgmXb+HpwVkZdoIrkn9isW7nRw/TZYaagZArAWyhfqsSDL/c9aTx7JUjGZCtYExRqCzAwGblwr6aFQ84nTo6qZ7XCeCVQNckE/KSWolvoQnxeoFFgIh8G/nA+kBAxxuQO5m9eFrwLIGJHgcyM63VFMhRSgNVyJr7og8y1vbTQpH8DIEVgxuYuexw0QECIalq5FYgEmpkgoFYltU/lnrqDz5osirSFpF7lrHAFKSWHYfEs+mY/82UnAStyMlW8sUPsVIciTZgz3jV1ebg0CEOpgPF22s1z1YQYKSXPJ1hbAhR8T26WdLhkuVfAzPR+YO1Ox5n58SmCcF6e3uzAoHA77RkevJdWH/3+f2O9TGf3w3fWQ2Hw5F/13mcsWAT+vv6DK4kFApJ/d3d1k+kJtbCrmxXHS3n8ER6b3CQbAqaEHVra6sGxcXW4SovLx+empxapS//FfwD9kpMJjMMBBAAAAAASUVORK5CYII=\"),define(\"text!cockpit/ui/images/pins.png\",\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAQCAYAAABQrvyxAAAACXBIWXMAAAsTAAALEwEAmpwYAAAGYklEQVRIDbVWe0yURxCf/R735o6DO0FBe0RFsaL4iLXGIKa2SY3P6JGa2GpjlJjUV9NosbU++tYUbEnaQIrVaKJBG7WiNFQFUWO1UUEsVg2CAgoeHHLewcH32O58cBdQsX9Y5+7LfrszOzO/2ZnZj1BKgTBiIwVGVvKd49OVVYunDlXn6wdBKh+ogXrv+DOz1melIb+3LM5fNv2XPYE5EHY+L3PJljN5zavHpJjsQNsA/JJEgyC2+WTjy3b0GfoJW8O4aoHtDwiHQrj5lw1LLyyb1bp5zAjJTus9klrVpdD6TqH2ngVO+0dsRJnp06cLIYU4fx7NnRI3bu7UIYOeJ/McnuY88q3k62gc0S4Dgf5qhICQtIXS2lqD7BhSduPk3YfyzXaANhBBJDxYdUqCywB2qS4RdyUuSkTF/VJxcbH5j8N7/75RuFrN3Zh8OS8zqf5m4UpPeenOyP42dbtBeuvVnCdkK1e4PfPouX03mo9se+c33M8wqDk5Ofqed8REUTicQhbySUxp9u3KlMSHTtrFU6Kyn03lz15PPpW25vsZeYSIKyiVURcqeZJOH9lTNZLfnxRjU/uwrjbEUBWsapcSO2Hq4k0VfZg9EzxdDNCEjDxgNqRDme9umz/btwlsHRIEePHgAf73RdnHZ6LTuIUBN7OBQ+c1Fdnp6cZ1BQUdeRuWZi97o3ktDQQkVeFFzqJARd1A5a0Vr7ta6Kp6TZjtZ+NTIOoKF6qDrL7e0QQIUCiqMMKk8Z1Q/SCSKvzocf2B6NEN0SQn/kTO6fKJ0zqjZUlQBSpJ0GjR77w0aoc1Pr6S5/kVJrNpakV5hR+LWKN4t7sLX+p0rx2vqSta64olIulUKUgCSXLWE1R4KPPSj+5vhm2hdDOG+CkQBmhhyyKq6SaFYWTn5bB3QJRNz54AuXKn8TJjhu0Wbv+wNEKQjVhnmKopjo4FxXmetCRnC4F7BhCiCUepqAepRh0TM/gjjzOOSK2NgWZPc05qampRWJHb7dbOffep2ednzLzgczlbrQA6gHYF9BYDh9GY+FjddMweHMscmMuep07gXlMQoqw9ALoYu5MJsak9QmJA2IvAgVmoCRciooyPujJtNCv1uHt3TmK9gegFKrG9kh6oXwZiIEAtBIjORGKNTWR/WeW8XVkbjuJepLAyloM8LmTN//njKZPbraATZaLjCHEww9Ei4FFiPg6Ja5gT6gxYgLgnRDHRQwJXbz2GOw0d4A3K4GXlUtMahJjYVxiYbrwOmxIS10bFnIBOSi6Tl9Jgs0zbOEX18wyEwgLPMrxD1Y4aCK8kmTpgYcpAF27Mzs42Hjx4kA8BICUlJfKArR7LcEvTB1xEC9AoEw9OPagWkVU/D1oesmK6U911zEczMVe01oZjiMggg6ux2Qk379qh4rYKet4GjrhhwEteBgBrH8BssoXEtbHzPpSBRRSpqlNpgAiUoxzHKxLRszoVuggIisxaDQWZqkQvQjAoax3NbDbLLGuUEABNGedXqSyLRupXgDT5JfAGZNLio9B0X8Uiwk4w77MDc1D4yejjWtykPS3DX01UDCY/GPQcVDe0QYT0CIxGFvUorfvBxZsRfVrUuWruMBAb/lXCUofoFNZfzGJtowXOX0vwUSFK4BgyMKm6P6s9wQUZld+jrYyMDC0iIQDaJdG4IyZQfL3RfbFcCBIlRgc+u3CjaTApuZ9KsANgG8PNzHlWWD3tCxd6kafNNiFp5HAalAkkJ0SCV2H3CgOD9Nc/FqrXuyb0Eocvfhq171p5eyuJ1omKJEP5rQGe/FOOnXtq335z8YmvYo9cHb2t8spIb3lVSseZW46FlGY/Sk9P50P2w20UlWJUkUHIushfc5PXGAzCo0PlD2pnpCYfCXga3lu+fPlevEhWrVrFyrN/Orfv87FOW9tlqb2Kc9pV8DzioMk3UNUbXM+8B/ATBr8C8CKdvGXWGD/9sqm3dkxtzA4McMjHMB8D2ftheYXo+qzt3pXvz8/PP/vk+v8537V+yYW87Zu+RZ1ZbrexoKAA/SBpaWn4+aL5w5zGk+/jW59JiMkESW5urpiVlWXENRb1H/Yf2I9txIxz5IdkX3TsraukpsbQjz6090yb4XsAvQoRE0YvJdamtIIbOnRoUVlZ2ftsLVQzIdEXHntsaZdimssVfCpFui109+BnWPsXaWLI/zactygAAAAASUVORK5CYII=\"),define(\"text!cockpit/ui/images/plus.png\",\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAAZiS0dEANIA0gDS7KbF4AAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9kFGw4yFTwuJTkAAAH7SURBVCjPdZKxa1NRFMZ/956XZMgFyyMlCZRA4hBx6lBcQ00GoYi4tEstFPwLAs7iLDi7FWuHThaUggihBDI5OWRoQAmBQFISQgvvpbwX3rsOaR4K+o2H8zvfOZxPWWtZqVarGaAJPAEe3ZW/A1+Bd+1221v1qhW4vb1dA44mk0nZ8zyCIAAgk8lgjGF9fb0PHF5cXLQTsF6vP/c879P19TVBEJDJZBARAKIoSmpra2sYY561Wq3PqtFouMBgMBgYay3ZbJZ/yfd9tNaUSqUboOKISPPq6sqsVvZ9H4AvL34B8PTj/QSO45jpdHovn883Ha31znw+JwzDpCEMQx4UloM8zyOdTif3zudztNY7jog8DMMQpRRxHPPt5TCBAEZvxlyOFTsfykRRBICIlB2t9a21Nh3HMXEc8+d7VhJHWCwWyzcohdZaHBHpO46z6fs+IsLj94XECaD4unCHL8FsNouI/HRE5Nx13c3ZbIbWOnG5HKtl+53TSq7rIiLnand31wUGnU7HjEYjlFLJZN/3yRnL1FMYY8jlcmxtbd0AFel2u7dnZ2eXxpi9xWJBEASkUimstYgIQSSkUimKxSKVSgVjzN7p6emPJHL7+/s14KjX65WHwyGz2SxZbWNjg2q12gcOT05O2n9lFeDg4MAAr/4T8rfHx8dJyH8DvvbYGzKvWukAAAAASUVORK5CYII=\"),define(\"text!cockpit/ui/images/throbber.gif\",\"data:image/gif;base64,R0lGODlh3AATAPQAAP///wAAAL6+vqamppycnLi4uLKyssjIyNjY2MTExNTU1Nzc3ODg4OTk5LCwsLy8vOjo6Ozs7MrKyvLy8vT09M7Ozvb29sbGxtDQ0O7u7tbW1sLCwqqqqvj4+KCgoJaWliH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAA3AATAAAF/yAgjmRpnmiqrmzrvnAsz3Rt33iu73zv/8CgcEgECAaEpHLJbDqf0Kh0Sq1ar9isdjoQtAQFg8PwKIMHnLF63N2438f0mv1I2O8buXjvaOPtaHx7fn96goR4hmuId4qDdX95c4+RG4GCBoyAjpmQhZN0YGYFXitdZBIVGAoKoq4CG6Qaswi1CBtkcG6ytrYJubq8vbfAcMK9v7q7D8O1ycrHvsW6zcTKsczNz8HZw9vG3cjTsMIYqQgDLAQGCQoLDA0QCwUHqfYSFw/xEPz88/X38Onr14+Bp4ADCco7eC8hQYMAEe57yNCew4IVBU7EGNDiRn8Z831cGLHhSIgdE/9chIeBgDoB7gjaWUWTlYAFE3LqzDCTlc9WOHfm7PkTqNCh54rePDqB6M+lR536hCpUqs2gVZM+xbrTqtGoWqdy1emValeXKwgcWABB5y1acFNZmEvXwoJ2cGfJrTv3bl69Ffj2xZt3L1+/fw3XRVw4sGDGcR0fJhxZsF3KtBTThZxZ8mLMgC3fRatCLYMIFCzwLEprg84OsDus/tvqdezZf13Hvr2B9Szdu2X3pg18N+68xXn7rh1c+PLksI/Dhe6cuO3ow3NfV92bdArTqC2Ebc3A8vjf5QWf15Bg7Nz17c2fj69+fnq+8N2Lty+fuP78/eV2X13neIcCeBRwxorbZrAxAJoCDHbgoG8RTshahQ9iSKEEzUmYIYfNWViUhheCGJyIP5E4oom7WWjgCeBBAJNv1DVV01MZdJhhjdkplWNzO/5oXI846njjVEIqR2OS2B1pE5PVscajkxhMycqLJgxQCwT40PjfAV4GqNSXYdZXJn5gSkmmmmJu1aZYb14V51do+pTOCmA00AqVB4hG5IJ9PvYnhIFOxmdqhpaI6GeHCtpooisuutmg+Eg62KOMKuqoTaXgicQWoIYq6qiklmoqFV0UoeqqrLbq6quwxirrrLTWauutJ4QAACH5BAkKAAAALAAAAADcABMAAAX/ICCOZGmeaKqubOu+cCzPdG3feK7vfO//wKBwSAQIBoSkcslsOp/QqHRKrVqv2Kx2OhC0BAXHx/EoCzboAcdhcLDdgwJ6nua03YZ8PMFPoBMca215eg98G36IgYNvDgOGh4lqjHd7fXOTjYV9nItvhJaIfYF4jXuIf4CCbHmOBZySdoOtj5eja59wBmYFXitdHhwSFRgKxhobBgUPAmdoyxoI0tPJaM5+u9PaCQZzZ9gP2tPcdM7L4tLVznPn6OQb18nh6NV0fu3i5OvP8/nd1qjwaasHcIPAcf/gBSyAAMMwBANYEAhWYQGDBhAyLihwYJiEjx8fYMxIcsGDAxVA/yYIOZIkBAaGPIK8INJlRpgrPeasaRPmx5QgJfB0abLjz50tSeIM+pFmUo0nQQIV+vRlTJUSnNq0KlXCSq09ozIFexEBAYkeNiwgOaEtn2LFpGEQsKCtXbcSjOmVlqDuhAx3+eg1Jo3u37sZBA9GoMAw4MB5FyMwfLht4sh7G/utPGHlYAV8Nz9OnOBz4c2VFWem/Pivar0aKCP2LFn2XwhnVxBwsPbuBAQbEGiIFg1BggoWkidva5z4cL7IlStfkED48OIYoiufYIH68+cKPkqfnsB58ePjmZd3Dj199/XE20tv6/27XO3S6z9nPCz9BP3FISDefL/Bt192/uWmAv8BFzAQAQUWWFaaBgqA11hbHWTIXWIVXifNhRlq6FqF1sm1QQYhdiAhbNEYc2KKK1pXnAIvhrjhBh0KxxiINlqQAY4UXjdcjSJyeAx2G2BYJJD7NZQkjCPKuCORKnbAIXsuKhlhBxEomAIBBzgIYXIfHfmhAAyMR2ZkHk62gJoWlNlhi33ZJZ2cQiKTJoG05Wjcm3xith9dcOK5X51tLRenoHTuud2iMnaolp3KGXrdBo7eKYF5p/mXgJcogClmcgzAR5gCKymXYqlCgmacdhp2UCqL96mq4nuDBTmgBasaCFp4sHaQHHUsGvNRiiGyep1exyIra2mS7dprrtA5++z/Z8ZKYGuGsy6GqgTIDvupRGE+6CO0x3xI5Y2mOTkBjD4ySeGU79o44mcaSEClhglgsKyJ9S5ZTGY0Bnzrj+3SiKK9Rh5zjAALCywZBk/ayCWO3hYM5Y8Dn6qxxRFsgAGoJwwgDQRtYXAAragyQOmaLKNZKGaEuUlpyiub+ad/KtPqpntypvvnzR30DBtjMhNodK6Eqrl0zU0/GjTUgG43wdN6Ra2pAhGtAAZGE5Ta8TH6wknd2IytNKaiZ+Or79oR/tcvthIcAPe7DGAs9Edwk6r3qWoTaNzY2fb9HuHh2S343Hs1VIHhYtOt+Hh551rh24vP5YvXSGzh+eeghy76GuikU9FFEainrvrqrLfu+uuwxy777LTXfkIIACH5BAkKAAAALAAAAADcABMAAAX/ICCOZGmeaKqubOu+cCzPdG3feK7vfO//wKBwSAQIBoSkcslsOp/QqHRKrVqv2Kx2OhC0BAWHB2l4CDZo9IDjcBja7UEhTV+3DXi3PJFA8xMcbHiDBgMPG31pgHBvg4Z9iYiBjYx7kWocb26OD398mI2EhoiegJlud4UFiZ5sm6Kdn2mBr5t7pJ9rlG0cHg5gXitdaxwFGArIGgoaGwYCZ3QFDwjU1AoIzdCQzdPV1c0bZ9vS3tUJBmjQaGXl1OB0feze1+faiBvk8wjnimn55e/o4OtWjp+4NPIKogsXjaA3g/fiGZBQAcEAFgQGOChgYEEDCCBBLihwQILJkxIe/3wMKfJBSQkJYJpUyRIkgwcVUJq8QLPmTYoyY6ZcyfJmTp08iYZc8MBkhZgxk9aEcPOlzp5FmwI9KdWn1qASurJkClRoWKwhq6IUqpJBAwQEMBYroAHkhLt3+RyzhgCDgAV48Wbgg+waAnoLMgTOm6DwQ8CLBzdGdvjw38V5JTg2lzhyTMeUEwBWHPgzZc4TSOM1bZia6LuqJxCmnOxv7NSsl1mGHHiw5tOuIWeAEHcFATwJME/ApgFBc3MVLEgPvE+Ddb4JokufPmFBAuvPXWu3MIF89wTOmxvOvp179evQtwf2nr6aApPyzVd3jn089e/8xdfeXe/xdZ9/d1ngHf98lbHH3V0LMrgPgsWpcFwBEFBgHmyNXWeYAgLc1UF5sG2wTHjIhNjBiIKZCN81GGyQwYq9uajeMiBOQGOLJ1KjTI40kmfBYNfc2NcGIpI4pI0vyrhjiT1WFqOOLEIZnjVOVpmajYfBiCSNLGbA5YdOkjdihSkQwIEEEWg4nQUmvYhYe+bFKaFodN5lp3rKvJYfnBKAJ+gGDMi3mmbwWYfng7IheuWihu5p32XcSWdSj+stkF95dp64jJ+RBipocHkCCp6PCiRQ6INookCAAwy0yd2CtNET3Yo7RvihBjFZAOaKDHT43DL4BQnsZMo8xx6uI1oQrHXXhHZrB28G62n/YSYxi+uzP2IrgbbHbiaer7hCiOxDFWhrbmGnLVuus5NFexhFuHLX6gkEECorlLpZo0CWJG4pLjIACykmBsp0eSSVeC15TDJeUhlkowlL+SWLNJpW2WEF87urXzNWSZ6JOEb7b8g1brZMjCg3ezBtWKKc4MvyEtwybPeaMAA1ECRoAQYHYLpbeYYCLfQ+mtL5c9CnfQpYpUtHOSejEgT9ogZ/GSqd0f2m+LR5WzOtHqlQX1pYwpC+WbXKqSYtpJ5Mt4a01lGzS3akF60AxkcTaLgAyRBPWCoDgHfJqwRuBuzdw/1ml3iCwTIeLUWJN0v4McMe7uasCTxseNWPSxc5RbvIgD7geZLbGrqCG3jepUmbbze63Y6fvjiOylbwOITPfIHEFsAHL/zwxBdvPBVdFKH88sw37/zz0Ecv/fTUV2/99SeEAAAh+QQJCgAAACwAAAAA3AATAAAF/yAgjmRpnmiqrmzrvnAsz3Rt33iu73zv/8CgcEgECAaEpHLJbDqf0Kh0Sq1ar9isdjoQtAQFh2cw8BQEm3T6yHEYHHD4oKCuD9qGvNsxT6QTgAkcHHmFeX11fm17hXwPG35qgnhxbwMPkXaLhgZ9gWp3bpyegX4DcG+inY+Qn6eclpiZkHh6epetgLSUcBxlD2csXXdvBQrHGgoaGhsGaIkFDwjTCArTzX+QadHU3c1ofpHc3dcGG89/4+TYktvS1NYI7OHu3fEJ5tpqBu/k+HX7+nXDB06SuoHm0KXhR65cQT8P3FRAMIAFgVMPwDCAwLHjggIHJIgceeFBg44eC/+ITCCBZYKSJ1FCWPBgpE2YMmc+qNCypwScMmnaXAkUJYOaFVyKLOqx5tCXJnMelcBzJNSYKIX2ZPkzqsyjPLku9Zr1QciVErYxaICAgEUOBRJIgzChbt0MLOPFwyBggV27eCUcmxZvg9+/dfPGo5bg8N/Ag61ZM4w4seDF1fpWhizZmoa+GSortgcaMWd/fkP/HY0MgWbTipVV++wY8GhvqSG4XUEgoYTKE+Qh0OCvggULiBckWEZ4Ggbjx5HXVc58IPQJ0idQJ66XanTpFraTe348+XLizRNcz658eHMN3rNPT+C+G/nodqk3t6a+fN3j+u0Xn3nVTQPfdRPspkL/b+dEIN8EeMm2GAYbTNABdrbJ1hyFFv5lQYTodSZABhc+loCEyhxTYYkZopdMMiNeiBxyIFajV4wYHpfBBspUl8yKHu6ooV5APsZjQxyyeNeJ3N1IYod38cgdPBUid6GCKfRWgAYU4IccSyHew8B3doGJHmMLkGkZcynKk2Z50Ym0zJzLbDCmfBbI6eIyCdyJmJmoqZmnBAXy9+Z/yOlZDZpwYihnj7IZpuYEevrYJ5mJEuqiof4l+NYDEXQpXQcMnNjZNDx1oGqJ4S2nF3EsqWrhqqVWl6JIslpAK5MaIqDeqjJq56qN1aTaQaPbHTPYr8Be6Gsyyh6Da7OkmmqP/7GyztdrNVQBm5+pgw3X7aoYKhfZosb6hyUKBHCgQKij1rghkOAJuZg1SeYIIY+nIpDvf/sqm4yNG5CY64f87qdAwSXKGqFkhPH1ZHb2EgYtw3bpKGVkPz5pJAav+gukjB1UHE/HLNJobWcSX8jiuicMMBFd2OmKwQFs2tjXpDfnPE1j30V3c7iRHlrzBD2HONzODyZtsQJMI4r0AUNaE3XNHQw95c9GC001MpIxDacFQ+ulTNTZlU3O1eWVHa6vb/pnQUUrgHHSBKIuwG+bCPyEqbAg25gMVV1iOB/IGh5YOKLKIQ6xBAcUHmzjIcIqgajZ+Ro42DcvXl7j0U4WOUd+2IGu7DWjI1pt4DYq8BPm0entuGSQY/4tBi9Ss0HqfwngBQtHbCH88MQXb/zxyFfRRRHMN+/889BHL/301Fdv/fXYZ39CCAAh+QQJCgAAACwAAAAA3AATAAAF/yAgjmRpnmiqrmzrvnAsz3Rt33iu73zv/8CgcEgECAaEpHLJbDqf0Kh0Sq1ar9isdjoQtAQFh2fAKXsKm7R6Q+Y43vABep0mGwwOPH7w2CT+gHZ3d3lyagl+CQNvg4yGh36LcHoGfHR/ZYOElQ9/a4ocmoRygIiRk5p8pYmZjXePaYBujHoOqp5qZHBlHAUFXitddg8PBg8KGsgayxvGkAkFDwgICtPTzX2mftHW3QnOpojG3dbYkNjk1waxsdDS1N7ga9zw1t/aifTk35fu6Qj3numL14fOuHTNECHqU4DDgQEsCCwidiHBAwYQMmpcUOCAhI8gJVzUuLGThAQnP/9abEAyI4MCIVOKZNnyJUqUJxNcGNlywYOQgHZirGkSJ8gHNEky+AkS58qWEJYC/bMzacmbQHkqNdlUJ1KoSz2i9COhmQYCEXtVrCBgwYS3cCf8qTcNQ9u4cFFOq2bPLV65Cf7dxZthbjW+CgbjnWtNgWPFcAsHdoxgWWK/iyV045sAc2S96SDn1exYw17REwpLQEYt2eW/qtPZRQAB7QoC61RW+GsBwYZ/CXb/XRCYLsAKFizEtUAc+G7lcZsjroscOvTmsoUvx15PwccJ0N8yL17N9PG/E7jv9S4hOV7pdIPDdZ+ePDzv2qMXn2b5+wTbKuAWnF3oZbABZY0lVmD/ApQd9thybxno2GGuCVDggaUpoyBsB1bGGgIYbJCBcuFJiOAyGohIInQSmmdeiBnMF2GHfNUlIoc1rncjYRjW6NgGf3VQGILWwNjBfxEZcAFbC7gHXQcfUYOYdwzQNxo5yUhQZXhvRYlMeVSuSOJHKJa5AQMQThBlZWZ6Bp4Fa1qzTAJbijcBlJrtxeaZ4lnnpZwpukWieGQmYx5ATXIplwTL8DdNZ07CtWYybNIJF4Ap4NZHe0920AEDk035kafieQrqXofK5ympn5JHKYjPrfoWcR8WWQGp4Ul32KPVgXdnqxM6OKqspjIYrGPDrlrsZtRIcOuR86nHFwbPvmes/6PH4frrqbvySh+mKGhaAARPzjjdhCramdoGGOhp44i+zogBkSDuWC5KlE4r4pHJkarXrj++Raq5iLmWLlxHBteavjG+6amJrUkJJI4Ro5sBv9AaOK+jAau77sbH7nspCwNIYIACffL7J4JtWQnen421nNzMcB6AqpRa9klonmBSiR4GNi+cJZpvwgX0ejj71W9yR+eIgaVvQgf0l/A8nWjUFhwtZYWC4hVnkZ3p/PJqNQ5NnwUQrQCGBBBMQIGTtL7abK+5JjAv1fi9bS0GLlJHgdjEgYzzARTwC1fgEWdJuKKBZzj331Y23qB3i9v5aY/rSUC4w7PaLeWXmr9NszMFoN79eeiM232o33EJAIzaSGwh++y012777bhT0UURvPfu++/ABy/88MQXb/zxyCd/QggAIfkECQoAAAAsAAAAANwAEwAABf8gII5kaZ5oqq5s675wLM90bd94ru987//AoHBIBAgGhKRyyWw6n9CodEqtWq/YrHY6ELQEBY5nwCk7xIWNer0hO95wziC9Ttg5b4ND/+Y87IBqZAaEe29zGwmJigmDfHoGiImTjXiQhJEPdYyWhXwDmpuVmHwOoHZqjI6kZ3+MqhyemJKAdo6Ge3OKbEd4ZRwFBV4rc4MPrgYPChrMzAgbyZSJBcoI1tfQoYsJydfe2amT3d7W0OGp1OTl0YtqyQrq0Lt11PDk3KGoG+nxBpvTD9QhwCctm0BzbOyMIwdOUwEDEgawIOCB2oMLgB4wgMCx44IHBySIHClBY0ePfyT/JCB5weRJCAwejFw58kGDlzBTqqTZcuPLmCIBiWx58+VHmiRLFj0JVCVLl0xl7qSZwCbOo0lFWv0pdefQrVFDJtr5gMBEYBgxqBWwYILbtxPsqMPAFu7blfa81bUbN4HAvXAzyLWnoDBguHIRFF6m4LBbwQngMYPXuC3fldbyPrMcGLM3w5wRS1iWWUNlvnElKDZtz/EEwaqvYahQoexEfyILi4RrYYKFZwJ3810QWZ2ECrx9Ew+O3K6F5Yq9zXbb+y30a7olJJ+wnLC16W97Py+uwdtx1NcLWzs/3G9e07stVPc9kHJ0BcLtQp+c3ewKAgYkUAFpCaAmmHqKLSYA/18WHEiZPRhsQF1nlLFWmIR8ZbDBYs0YZuCGpGXWmG92aWiPMwhEOOEEHXRwIALlwXjhio+BeE15IzpnInaLbZBBhhti9x2GbnVQo2Y9ZuCfCgBeMCB+DJDIolt4iVhOaNSJdCOBUfIlkmkyMpPAAvKJ59aXzTQzJo0WoJnmQF36Jp6W1qC4gWW9GZladCiyJd+KnsHImgRRVjfnaDEKuiZvbcYWo5htzefbl5LFWNeSKQAo1QXasdhiiwwUl2B21H3aQaghXnPcp1NagCqYslXAqnV+zYWcpNwVp9l5eepJnHqL4SdBi56CGlmw2Zn6aaiZjZqfb8Y2m+Cz1O0n3f+tnvrGbF6kToApCgAWoNWPeh754JA0vmajiAr4iOuOW7abQXVGNriBWoRdOK8FxNqLwX3oluubhv8yluRbegqGb536ykesuoXhyJqPQJIGbLvQhkcwjKs1zBvBwSZIsbcsDCCBAAf4ya+UEhyQoIiEJtfoZ7oxUOafE2BwgMWMqUydfC1LVtiArk0QtGkWEopzlqM9aJrKHfw5c6wKjFkmXDrbhwFockodtMGFLWpXy9JdiXN1ZDNszV4WSLQCGBKoQYHUyonqrHa4ErewAgMmcAAF7f2baIoVzC2p3gUvJtLcvIWqloy6/R04mIpLwDhciI8qLOB5yud44pHPLbA83hFDWPjNbuk9KnySN57Av+TMBvgEAgzzNhJb5K777rz37vvvVHRRxPDEF2/88cgnr/zyzDfv/PPQnxACACH5BAkKAAAALAAAAADcABMAAAX/ICCOZGmeaKqubOu+cCzPdG3feK7vfO//wKBwSAQIBoSkcslsOp/QqHRKrVqv2Kx2OhC0BIUCwcMpO84OT2HDbm8GHLQjnn6wE3g83SA3DB55G3llfHxnfnZ4gglvew6Gf4ySgmYGlpCJknochWiId3kJcZZyDn93i6KPl4eniopwq6SIoZKxhpenbhtHZRxhXisDopwPgHkGDxrLGgjLG8mC0gkFDwjX2AgJ0bXJ2djbgNJsAtbfCNB2oOnn6MmKbeXt226K1fMGi6j359D69ua+QZskjd+3cOvY9XNgp4ABCQNYEDBl7EIeCQkeMIDAseOCBwckiBSZ4ILGjh4B/40kaXIjSggMHmBcifHky5gYE6zM2OAlzGM6Z5rs+fIjTZ0tfcYMSlLCUJ8fL47kCVXmTjwPiKJkUCDnyqc3CxzQmYeAxAEGLGJYiwCDgAUT4sqdgOebArdw507IUNfuW71xdZ7DC5iuhGsKErf9CxhPYgUaEhPWyzfBMgUIJDPW6zhb5M1y+R5GjFkBaLmCM0dOfHqvztXYJnMejaFCBQlmVxAYsEGkYnQV4lqYMNyCtnYSggNekAC58uJxmTufW5w55mwKkg+nLp105uTC53a/nhg88fMTmDfDVl65Xum/IZt/3/zaag3a5W63nll1dvfiWbaaZLmpQIABCVQA2f9lAhTG112PQWYadXE9+FtmEwKWwQYQJrZagxomsOCAGVImInsSbpCBhhwug6KKcXXQQYUcYuDMggrASFmNzjjzzIrh7cUhhhHqONeGpSEW2QYxHsmjhxpgUGAKB16g4IIbMNCkXMlhaJ8GWVJo2I3NyKclYF1GxgyYDEAnXHJrMpNAm/rFBSczPiYAlwXF8ZnmesvoOdyMbx7m4o0S5LWdn4bex2Z4xYmEzaEb5EUcnxbA+WWglqIn6aHPTInCgVbdlZyMqMrIQHMRSiaBBakS1903p04w434n0loBoQFOt1yu2YAnY68RXiNsqh2s2qqxuyKb7Imtmgcrqsp6h8D/fMSpapldx55nwayK/SfqCQd2hcFdAgDp5GMvqhvakF4mZuS710WGIYy30khekRkMu92GNu6bo7r/ttjqwLaua5+HOdrKq5Cl3dcwi+xKiLBwwwom4b0E6xvuYyqOa8IAEghwQAV45VvovpkxBl2mo0W7AKbCZXoAhgMmWnOkEqx2JX5nUufbgJHpXCfMOGu2QAd8eitpW1eaNrNeMGN27mNz0swziYnpSbXN19gYtstzfXrdYjNHtAIYGFVwwAEvR1dfxdjKxVzAP0twAAW/ir2w3nzTd3W4yQWO3t0DfleB4XYnEHCEhffdKgaA29p0eo4fHLng9qoG+OVyXz0gMeWGY7qq3xhiRIEAwayNxBawxy777LTXbjsVXRSh++689+7778AHL/zwxBdv/PEnhAAAIfkECQoAAAAsAAAAANwAEwAABf8gII5kaZ5oqq5s675wLM90bd94ru987//AoHBIBAgGhKRyyWw6n9CodEqtWq/YrHY6ELQEhYLD4BlwHGg0ubBpuzdm9Dk9eCTu+MTZkDb4PXYbeIIcHHxqf4F3gnqGY2kOdQmCjHCGfpCSjHhmh2N+knmEkJmKg3uHfgaaeY2qn6t2i4t7sKAPbwIJD2VhXisDCQZgDrKDBQ8aGgjKyhvDlJMJyAjV1gjCunkP1NfVwpRtk93e2ZVt5NfCk27jD97f0LPP7/Dr4pTp1veLgvrx7AL+Q/BM25uBegoYkDCABYFhEobhkUBRwoMGEDJqXPDgQMUEFC9c1LjxQUUJICX/iMRIEgIDkycrjmzJMSXFlDNJvkwJsmdOjQwKfDz5M+PLoSGLQqgZU6XSoB/voHxawGbFlS2XGktAwKEADB0xiEWAodqGBRPSqp1wx5qCamDRrp2Qoa3bagLkzrULF4GCvHPTglRAmKxZvWsHayBcliDitHUlvGWM97FgCdYWVw4c2e/kw4HZJlCwmDBhwHPrjraGYTHqtaoxVKggoesKAgd2SX5rbUMFCxOAC8cGDwHFwBYWJCgu4XfwtcqZV0grPHj0u2SnqwU+IXph3rK5b1fOu7Bx5+K7L6/2/Xhg8uyXnQ8dvfRiDe7TwyfNuzlybKYpgIFtKhAgwEKkKcOf/wChZbBBgMucRh1so5XH3wbI1WXafRJy9iCErmX4IWHNaIAhZ6uxBxeGHXQA24P3yYfBBhmgSBozESpwongWOBhggn/N1aKG8a1YY2oVAklgCgQUUwGJ8iXAgItrWUARbwpqIOWEal0ZoYJbzmWlZCWSlsAC6VkwZonNbMAAl5cpg+NiZwpnJ0Xylegmlc+tWY1mjnGnZnB4QukMA9UJRxGOf5r4ppqDjjmnfKilh2ejGiyJAgF1XNmYbC2GmhZ5AcJVgajcXecNqM9Rx8B6bingnlotviqdkB3YCg+rtOaapFsUhSrsq6axJ6sEwoZK7I/HWpCsr57FBxJ1w8LqV/81zbkoXK3LfVeNpic0KRQG4NHoIW/XEmZuaiN6tti62/moWbk18uhjqerWS6GFpe2YVotskVssWfBOAHACrZHoWcGQwQhlvmsdXBZ/F9YLMF2jzUuYBP4a7CLCnoEHrgkDSCDAARUILAGaVVqAwQHR8pZXomm9/ONhgjrbgc2lyYxmpIRK9uSNjrXs8gEbTrYyl2ryTJmsLCdKkWzFQl1lWlOXGmifal6p9VnbQfpyY2SZyXKVV7JmZkMrgIFSyrIeUJ2r7YKnXdivUg1kAgdQ8B7IzJjGsd9zKSdwyBL03WpwDGxwuOASEP5vriO2F3nLjQdIrpaRDxqcBdgIHGA74pKrZXiR2ZWuZt49m+o3pKMC3p4Av7SNxBa456777rz37jsVXRQh/PDEF2/88cgnr/zyzDfv/PMnhAAAIfkECQoAAAAsAAAAANwAEwAABf8gII5kaZ5oqq5s675wLM90bd94ru987//AoHBIBAgGhKRyyWw6n9CodEqtWq/YrHY6ELQEhYLDUPAMHGi0weEpbN7wI8cxTzsGj4R+n+DUxwaBeBt7hH1/gYIPhox+Y3Z3iwmGk36BkIN8egOIl3h8hBuOkAaZhQlna4BrpnyWa4mleZOFjrGKcXoFA2ReKwMJBgISDw6abwUPGggazc0bBqG0G8kI1tcIwZp51djW2nC03d7BjG8J49jl4cgP3t/RetLp1+vT6O7v5fKhAvnk0UKFogeP3zmCCIoZkDCABQFhChQYuKBHgkUJkxpA2MhxQYEDFhNcvPBAI8eNCx7/gMQYckPJkxsZPLhIM8FLmDJrYiRp8mTKkCwT8IQJwSPQkENhpgQpEunNkzlpWkwKdSbGihKocowqVSvKWQkIOBSgQOYFDBgQpI0oYMGEt3AzTLKm4BqGtnDjirxW95vbvG/nWlub8G9euRsiqqWLF/AEkRoiprX2wLDeDQgkW9PQGLDgyNc665WguK8C0XAnRY6oGPUEuRLsgk5g+a3cCxUqSBC7gsCBBXcVq6swwULx4hayvctGPK8FCwsSLE9A3Hje6NOrHzeOnW695sffRi/9HfDz7sIVSNB+XXrmugo0rHcM3X388o6jr44ceb51uNjF1xcC8zk3wXiS8aYC/wESaLABBs7ch0ECjr2WAGvLsLZBeHqVFl9kGxooV0T81TVhBo6NiOEyJ4p4IYnNRBQiYCN6x4wCG3ZAY2If8jXjYRcyk2FmG/5nXAY8wqhWAii+1YGOSGLoY4VRfqiAgikwmIeS1gjAgHkWYLQZf9m49V9gDWYWY5nmTYCRM2TS5pxxb8IZGV5nhplmhJyZadxzbrpnZ2d/6rnZgHIid5xIMDaDgJfbLdrgMkKW+Rygz1kEZz1mehabkBpgiQIByVikwGTqVfDkk2/Vxxqiqur4X3fksHccre8xlxerDLiHjQIVUAgXr77yFeyuOvYqXGbMrbrqBMqaFpFFzhL7qv9i1FX7ZLR0LUNdcc4e6Cus263KbV+inkAAHhJg0BeITR6WmHcaxhvXg/AJiKO9R77ILF1FwmVdAu6WBu+ZFua72mkZWMfqBElKu0G8rFZ5n4ATp5jkmvsOq+Nj7u63ZMMPv4bveyYy6fDH+C6brgnACHBABQUrkGirz2FwAHnM4Mmhzq9yijOrOi/MKabH6VwBiYwZdukEQAvILKTWXVq0ZvH5/CfUM7M29Zetthp1eht0eqkFYw8IKXKA6mzXfTeH7fZg9zW0AhgY0TwthUa6Ch9dBeIsbsFrYkRBfgTfiG0FhwMWnbsoq3cABUYOnu/ejU/A6uNeT8u4wMb1WnBCyJJTLjjnr8o3OeJrUcpc5oCiPqAEkz8tXuLkPeDL3Uhs4fvvwAcv/PDEU9FFEcgnr/zyzDfv/PPQRy/99NRXf0IIACH5BAkKAAAALAAAAADcABMAAAX/ICCOZGmeaKqubOu+cCzPdG3feK7vfO//wKBwSAQIBoSkcslsOp/QqHRKrVqv2Kx2OhC0BIWCw/AoDziOtCHt8BQ28PjmzK57Hom8fo42+P8DeAkbeYQcfX9+gYOFg4d1bIGEjQmPbICClI9/YwaLjHAJdJeKmZOViGtpn3qOqZineoeJgG8CeWUbBV4rAwkGAhIVGL97hGACGsrKCAgbBoTRhLvN1c3PepnU1s2/oZO6AtzdBoPf4eMI3tIJyOnF0YwFD+nY8e3z7+Xfefnj9uz8cVsXCh89axgk7BrAggAwBQsYIChwQILFixIeNIDAseOCBwcSXMy2sSPHjxJE/6a0eEGjSY4MQGK86PIlypUJEmYsaTKmyJ8JW/Ls6HMkzaEn8YwMWtPkx4pGd76E4DMPRqFTY860OGhogwYagBFoKEABA46DEGBAoEBB0AUT4sqdIFKBNbcC4M6dkEEk22oYFOTdG9fvWrtsBxM23MytYL17666t9phwXwlum2lIDHmuSA2IGyuOLOHv38qLMbdFjHruZbWgRXeOe1nC2BUEDiyAMMHZuwoTLAQX3nvDOAUW5Vogru434d4JnAsnPmFB9NBshQXfa9104+Rxl8e13rZxN+CEydtVsFkd+vDjE7C/q52wOvb4s7+faz025frbxefWbSoQIAEDEUCwgf9j7bUlwHN9ZVaegxDK1xYzFMJH24L5saXABhlYxiEzHoKoIV8LYqAMaw9aZqFmJUK4YHuNfRjiXhmk+NcyJgaIolvM8BhiBx3IleN8lH1IWAcRgkZgCgYiaBGJojGgHHFTgtagAFYSZhF7/qnTpY+faVlNAnqJN0EHWa6ozAZjBtgmmBokwMB01LW5jAZwbqfmlNips4B4eOqJgDJ2+imXRZpthuigeC6XZTWIxilXmRo8iYKBCwiWmWkJVEAkfB0w8KI1IvlIpKnOkVpqdB5+h96o8d3lFnijrgprjbfGRSt0lH0nAZG5vsprWxYRW6Suq4UWqrLEsspWg8Io6yv/q6EhK0Fw0GLbjKYn5CZYBYht1laPrnEY67kyrhYbuyceiR28Pso7bYwiXjihjWsWuWF5p/H765HmNoiur3RJsGKNG/jq748XMrwmjhwCfO6QD9v7LQsDxPTAMKsFpthyJCdkmgYiw0VdXF/Om9dyv7YMWGXTLYpZg5wNR11C78oW3p8HSGgul4qyrJppgllJHJZHn0Y0yUwDXCXUNquFZNLKyYXBAVZvxtAKYIQEsmPgDacr0tltO1y/DMwYpkgUpJfTasLGzd3cdCN3gN3UWRcY3epIEPevfq+3njBxq/kqBoGBduvea8f393zICS63ivRBTqgFpgaWZEIUULdcK+frIfAAL2AjscXqrLfu+uuwx05FF0XUbvvtuOeu++689+7778AHL/wJIQAAOwAAAAAAAAAAAA==\")"
  },
  {
    "path": "dirigible/shared/static/ace/mode-python.js",
    "content": "define(\"ace/mode/python\",function(a,b,c){var d=a(\"pilot/oop\"),e=a(\"ace/mode/text\").Mode,f=a(\"ace/tokenizer\").Tokenizer,g=a(\"ace/mode/python_highlight_rules\").PythonHighlightRules,h=a(\"ace/mode/matching_brace_outdent\").MatchingBraceOutdent,i=a(\"ace/range\").Range,j=function(){this.$tokenizer=new f((new g).getRules()),this.$outdent=new h};d.inherits(j,e),function(){this.toggleCommentLines=function(a,b,c,d){var e=!0,f=[],g=/^(\\s*)#/;for(var h=c;h<=d;h++)if(!g.test(b.getLine(h))){e=!1;break}if(e){var j=new i(0,0,0,0);for(var h=c;h<=d;h++){var k=b.getLine(h),l=k.match(g);j.start.row=h,j.end.row=h,j.end.column=l[0].length,b.replace(j,l[1])}}else b.indentRows(c,d,\"#\")},this.getNextLineIndent=function(a,b,c){var d=this.$getIndent(b),e=this.$tokenizer.getLineTokens(b,a),f=e.tokens,g=e.state;if(f.length&&f[f.length-1].type==\"comment\")return d;if(a==\"start\"){var h=b.match(/^.*[\\{\\(\\[\\:]\\s*$/);h&&(d+=c)}return d},this.checkOutdent=function(a,b,c){return this.$outdent.checkOutdent(b,c)},this.autoOutdent=function(a,b,c){this.$outdent.autoOutdent(b,c)}}.call(j.prototype),b.Mode=j}),define(\"ace/mode/python_highlight_rules\",function(a,b,c){var d=a(\"pilot/oop\"),e=a(\"pilot/lang\"),f=a(\"ace/mode/text_highlight_rules\").TextHighlightRules,g=function(){var a=e.arrayToMap(\"and|as|assert|break|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|not|or|pass|print|raise|return|try|while|with|yield\".split(\"|\")),b=e.arrayToMap(\"True|False|None|NotImplemented|Ellipsis|__debug__\".split(\"|\")),c=e.arrayToMap(\"abs|divmod|input|open|staticmethod|all|enumerate|int|ord|str|any|eval|isinstance|pow|sum|basestring|execfile|issubclass|print|super|binfile|iter|property|tuple|bool|filter|len|range|type|bytearray|float|list|raw_input|unichr|callable|format|locals|reduce|unicode|chr|frozenset|long|reload|vars|classmethod|getattr|map|repr|xrange|cmp|globals|max|reversed|zip|compile|hasattr|memoryview|round|__import__|complex|hash|min|set|apply|delattr|help|next|setattr|buffer|dict|hex|object|slice|coerce|dir|id|oct|sorted|intern\".split(\"|\")),d=e.arrayToMap(\"\".split(\"|\")),f=\"(?:r|u|ur|R|U|UR|Ur|uR)?\",g=\"(?:(?:[1-9]\\\\d*)|(?:0))\",h=\"(?:0[oO]?[0-7]+)\",i=\"(?:0[xX][\\\\dA-Fa-f]+)\",j=\"(?:0[bB][01]+)\",k=\"(?:\"+g+\"|\"+h+\"|\"+i+\"|\"+j+\")\",l=\"(?:[eE][+-]?\\\\d+)\",m=\"(?:\\\\.\\\\d+)\",n=\"(?:\\\\d+)\",o=\"(?:(?:\"+n+\"?\"+m+\")|(?:\"+n+\"\\\\.))\",p=\"(?:(?:\"+o+\"|\"+n+\")\"+l+\")\",q=\"(?:\"+p+\"|\"+o+\")\";this.$rules={start:[{token:\"comment\",regex:\"#.*$\"},{token:\"string\",regex:f+'\"{3}(?:[^\\\\\\\\]|\\\\\\\\.)*?\"{3}'},{token:\"string\",regex:f+'\"{3}.*$',next:\"qqstring\"},{token:\"string\",regex:f+'\"(?:[^\\\\\\\\]|\\\\\\\\.)*?\"'},{token:\"string\",regex:f+\"'{3}(?:[^\\\\\\\\]|\\\\\\\\.)*?'{3}\"},{token:\"string\",regex:f+\"'{3}.*$\",next:\"qstring\"},{token:\"string\",regex:f+\"'(?:[^\\\\\\\\]|\\\\\\\\.)*?'\"},{token:\"constant.numeric\",regex:\"(?:\"+q+\"|\\\\d+)[jJ]\\\\b\"},{token:\"constant.numeric\",regex:q},{token:\"constant.numeric\",regex:k+\"[lL]\\\\b\"},{token:\"constant.numeric\",regex:k+\"\\\\b\"},{token:function(e){return a.hasOwnProperty(e)?\"keyword\":b.hasOwnProperty(e)?\"constant.language\":d.hasOwnProperty(e)?\"invalid.illegal\":c.hasOwnProperty(e)?\"support.function\":e==\"debugger\"?\"invalid.deprecated\":\"identifier\"},regex:\"[a-zA-Z_$][a-zA-Z0-9_$]*\\\\b\"},{token:\"keyword.operator\",regex:\"\\\\+|\\\\-|\\\\*|\\\\*\\\\*|\\\\/|\\\\/\\\\/|%|<<|>>|&|\\\\||\\\\^|~|<|>|<=|=>|==|!=|<>|=\"},{token:\"lparen\",regex:\"[\\\\[\\\\(\\\\{]\"},{token:\"rparen\",regex:\"[\\\\]\\\\)\\\\}]\"},{token:\"text\",regex:\"\\\\s+\"}],qqstring:[{token:\"string\",regex:'(?:[^\\\\\\\\]|\\\\\\\\.)*?\"{3}',next:\"start\"},{token:\"string\",regex:\".+\"}],qstring:[{token:\"string\",regex:\"(?:[^\\\\\\\\]|\\\\\\\\.)*?'{3}\",next:\"start\"},{token:\"string\",regex:\".+\"}]}};d.inherits(g,f),b.PythonHighlightRules=g})"
  },
  {
    "path": "dirigible/shared/static/ace/worker-javascript.js",
    "content": "function initSender(){var a=require(\"pilot/event_emitter\").EventEmitter,b=require(\"pilot/oop\"),c=function(){};(function(){b.implement(this,a),this.callback=function(a,b){postMessage({type:\"call\",id:b,data:a})},this.emit=function(a,b){postMessage({type:\"event\",name:a,data:b})}}).call(c.prototype);return new c}function initBaseUrls(a){require.tlns=a}var console={log:function(a){postMessage({type:\"log\",data:a})}},window={console:console},require=function(a){var b=require.modules[a];if(b){b.initialized||(b.exports=b.factory().exports,b.initialized=!0);return b.exports}var c=a.split(\"/\");c[0]=require.tlns[c[0]]||c[0],path=c.join(\"/\")+\".js\",require.id=a,importScripts(path);return require(a)};require.modules={},require.tlns={};var define=function(a,b,c){arguments.length==2?c=b:arguments.length==1&&(c=a,a=require.id);a.indexOf(\"text/\")!==0&&(require.modules[a]={factory:function(){var a={exports:{}},b=c(require,a.exports,a);b&&(a.exports=exports);return a}})},main,sender;onmessage=function(a){var b=a.data;if(b.command)main[b.command].apply(main,b.args);else if(b.init){initBaseUrls(b.tlns),require(\"pilot/fixoldbrowsers\"),sender=initSender();var c=require(b.module)[b.classname];main=new c(sender)}else b.event&&sender._dispatchEvent(b.event,b.data)},define(\"pilot/fixoldbrowsers\",function(a,b,c){var d=Object.prototype.hasOwnProperty;Array.isArray||(Array.isArray=function(a){return Object.prototype.toString.call(a)==\"[object Array]\"}),Array.prototype.forEach||(Array.prototype.forEach=function(a,b){var c=+this.length;for(var d=0;d<c;d++)d in this&&a.call(b,this[d],d,this)}),Array.prototype.map||(Array.prototype.map=function(a){var b=+this.length;if(typeof a!=\"function\")throw new TypeError;var c=Array(b),d=arguments[1];for(var e=0;e<b;e++)e in this&&(c[e]=a.call(d,this[e],e,this));return c}),Array.prototype.filter||(Array.prototype.filter=function(a){var b=[],c=arguments[1];for(var d=0;d<this.length;d++)a.call(c,this[d])&&b.push(this[d]);return b}),Array.prototype.every||(Array.prototype.every=function(a){var b=arguments[1];for(var c=0;c<this.length;c++)if(!a.call(b,this[c]))return!1;return!0}),Array.prototype.some||(Array.prototype.some=function(a){var b=arguments[1];for(var c=0;c<this.length;c++)if(a.call(b,this[c]))return!0;return!1}),Array.prototype.reduce||(Array.prototype.reduce=function(a){var b=+this.length;if(typeof a!=\"function\")throw new TypeError;if(b==0&&arguments.length==1)throw new TypeError;var c=0;if(arguments.length>=2)var d=arguments[1];else do{if(c in this){d=this[c++];break}if(++c>=b)throw new TypeError}while(!0);for(;c<b;c++)c in this&&(d=a.call(null,d,this[c],c,this));return d}),Array.prototype.reduceRight||(Array.prototype.reduceRight=function(a){var b=+this.length;if(typeof a!=\"function\")throw new TypeError;if(b==0&&arguments.length==1)throw new TypeError;var c=b-1;if(arguments.length>=2)var d=arguments[1];else do{if(c in this){d=this[c--];break}if(--c<0)throw new TypeError}while(!0);for(;c>=0;c--)c in this&&(d=a.call(null,d,this[c],c,this));return d}),Array.prototype.indexOf||(Array.prototype.indexOf=function(a){var b=this.length;if(!b)return-1;var c=arguments[1]||0;if(c>=b)return-1;c<0&&(c+=b);for(;c<b;c++){if(!d.call(this,c))continue;if(a===this[c])return c}return-1}),Array.prototype.lastIndexOf||(Array.prototype.lastIndexOf=function(a){var b=this.length;if(!b)return-1;var c=arguments[1]||b;c<0&&(c+=b),c=Math.min(c,b-1);for(;c>=0;c--){if(!d.call(this,c))continue;if(a===this[c])return c}return-1}),Object.getPrototypeOf||(Object.getPrototypeOf=function(a){return a.__proto__||a.constructor.prototype}),Object.getOwnPropertyDescriptor||(Object.getOwnPropertyDescriptor=function(a,b){if(typeof a!==\"object\"&&typeof a!==\"function\"||a===null)throw new TypeError(\"Object.getOwnPropertyDescriptor called on a non-object\");return d.call(a,b)?{value:a[b],enumerable:!0,configurable:!0,writeable:!0}:undefined}),Object.getOwnPropertyNames||(Object.getOwnPropertyNames=function(a){return Object.keys(a)}),Object.create||(Object.create=function(a,b){var c;if(a===null)c={\"__proto__\":null};else{if(typeof a!=\"object\")throw new TypeError(\"typeof prototype[\"+typeof a+\"] != 'object'\");var d=function(){};d.prototype=a,c=new d}typeof b!==\"undefined\"&&Object.defineProperties(c,b);return c}),Object.defineProperty||(Object.defineProperty=function(a,b,c){if(typeof c==\"object\"&&a.__defineGetter__){if(d.call(c,\"value\")){!a.__lookupGetter__(b)&&!a.__lookupSetter__(b)&&(a[b]=c.value);if(d.call(c,\"get\")||d.call(c,\"set\"))throw new TypeError(\"Object doesn't support this action\")}else typeof c.get==\"function\"&&a.__defineGetter__(b,c.get);typeof c.set==\"function\"&&a.__defineSetter__(b,c.set)}return a}),Object.defineProperties||(Object.defineProperties=function(a,b){for(var c in b)d.call(b,c)&&Object.defineProperty(a,c,b[c]);return a}),Object.seal||(Object.seal=function(a){return a}),Object.freeze||(Object.freeze=function(a){return a});try{Object.freeze(function(){})}catch(e){Object.freeze=function(a){return function(b){return typeof b==\"function\"?b:a(b)}}(Object.freeze)}Object.preventExtensions||(Object.preventExtensions=function(a){return a}),Object.isSealed||(Object.isSealed=function(a){return!1}),Object.isFrozen||(Object.isFrozen=function(a){return!1}),Object.isExtensible||(Object.isExtensible=function(a){return!0});if(!Object.keys){var f=!0,g=[\"toString\",\"toLocaleString\",\"valueOf\",\"hasOwnProperty\",\"isPrototypeOf\",\"propertyIsEnumerable\",\"constructor\"],h=g.length;for(var i in {toString:null})f=!1;Object.keys=function(a){if(typeof a!==\"object\"&&typeof a!==\"function\"||a===null)throw new TypeError(\"Object.keys called on a non-object\");var b=[];for(var c in a)d.call(a,c)&&b.push(c);if(f)for(var e=0,i=h;e<i;e++){var j=g[e];d.call(a,j)&&b.push(j)}return b}}Date.prototype.toISOString||(Date.prototype.toISOString=function(){return this.getUTCFullYear()+\"-\"+(this.getUTCMonth()+1)+\"-\"+this.getUTCDate()+\"T\"+this.getUTCHours()+\":\"+this.getUTCMinutes()+\":\"+this.getUTCSeconds()+\"Z\"}),Date.now||(Date.now=function(){return(new Date).getTime()}),Date.prototype.toJSON||(Date.prototype.toJSON=function(a){if(typeof this.toISOString!=\"function\")throw new TypeError;return this.toISOString()}),isNaN(Date.parse(\"T00:00\"))&&(Date=function(a){var b=function(c,d,e,f,g,h,i){var j=arguments.length;if(this instanceof a){var k=j===1&&String(c)===c?new a(b.parse(c)):j>=7?new a(c,d,e,f,g,h,i):j>=6?new a(c,d,e,f,g,h):j>=5?new a(c,d,e,f,g):j>=4?new a(c,d,e,f):j>=3?new a(c,d,e):j>=2?new a(c,d):j>=1?new a(c):new a;k.constructor=b;return k}return a.apply(this,arguments)},c=new RegExp(\"^(?:((?:[+-]\\\\d\\\\d)?\\\\d\\\\d\\\\d\\\\d)(?:-(\\\\d\\\\d)(?:-(\\\\d\\\\d))?)?)?(?:T(\\\\d\\\\d):(\\\\d\\\\d)(?::(\\\\d\\\\d)(?:\\\\.(\\\\d\\\\d\\\\d))?)?)?(?:Z|([+-])(\\\\d\\\\d):(\\\\d\\\\d))?$\");for(var d in a)b[d]=a[d];b.now=a.now,b.UTC=a.UTC,b.prototype=a.prototype,b.prototype.constructor=b,b.parse=function(b){var d=c.exec(b);if(d){d.shift();var e=d[0]===undefined;for(var f=0;f<10;f++){if(f===7)continue;d[f]=+(d[f]||(f<3?1:0)),f===1&&d[f]--}if(e)return((d[3]*60+d[4])*60+d[5])*1e3+d[6];var g=(d[8]*60+d[9])*60*1e3;d[6]===\"-\"&&(g=-g);return a.UTC.apply(this,d.slice(0,7))+g}return a.parse.apply(this,arguments)};return b}(Date));var j=Array.prototype.slice;Function.prototype.bind||(Function.prototype.bind=function(a){var b=this;if(typeof b.apply!=\"function\"||typeof b.call!=\"function\")return new TypeError;var c=j.call(arguments),d=function(){if(this instanceof d){var a=Object.create(b.prototype);b.apply(a,c.concat(j.call(arguments)));return a}return b.call.apply(b,c.concat(j.call(arguments)))};d.bound=b,d.boundTo=a,d.boundArgs=c,d.length=typeof b==\"function\"?Math.max(b.length-c.length,0):0;return d});if(!String.prototype.trim){var k=/^\\s\\s*/,l=/\\s\\s*$/;String.prototype.trim=function(){return String(this).replace(k,\"\").replace(l,\"\")}}b.globalsLoaded=!0}),define(\"pilot/event_emitter\",function(a,b,c){var d={};d._emit=d._dispatchEvent=function(a,b){this._eventRegistry=this._eventRegistry||{};var c=this._eventRegistry[a];if(!!c&&!!c.length){var b=b||{};b.type=a;for(var d=0;d<c.length;d++)c[d](b)}},d.on=d.addEventListener=function(a,b){this._eventRegistry=this._eventRegistry||{};var c=this._eventRegistry[a];if(!c)var c=this._eventRegistry[a]=[];c.indexOf(b)==-1&&c.push(b)},d.removeListener=d.removeEventListener=function(a,b){this._eventRegistry=this._eventRegistry||{};var c=this._eventRegistry[a];if(!!c){var d=c.indexOf(b);d!==-1&&c.splice(d,1)}},d.removeAllListeners=function(a){this._eventRegistry&&(this._eventRegistry[a]=[])},b.EventEmitter=d}),define(\"pilot/oop\",function(a,b,c){b.inherits=function(){var a=function(){};return function(b,c){a.prototype=c.prototype,b.super_=c.prototype,b.prototype=new a,b.prototype.constructor=b}}(),b.mixin=function(a,b){for(var c in b)a[c]=b[c]},b.implement=function(a,c){b.mixin(a,c)}}),define(\"ace/mode/javascript_worker\",function(a,b,c){var d=a(\"pilot/oop\"),e=a(\"ace/worker/mirror\").Mirror,f=a(\"ace/worker/jshint\").JSHINT,g=b.JavaScriptWorker=function(a){e.call(this,a),this.setTimeout(500)};d.inherits(g,e),function(){this.onUpdate=function(){var b=this.doc.getValue();b=b.replace(/^#!.*\\n/,\"\\n\");var c=a(\"ace/narcissus/jsparse\");try{c.parse(b)}catch(d){sender.emit(\"narcissus\",{row:d.lineno-1,column:null,text:d.message,type:\"error\"});return}finally{}f(b,{undef:!1,onevar:!1,passfail:!1}),this.sender.emit(\"jslint\",f.errors)}}.call(g.prototype)}),define(\"ace/worker/mirror\",function(a,b,c){var d=a(\"ace/document\").Document,e=a(\"pilot/lang\"),f=b.Mirror=function(a){this.sender=a;var b=this.doc=new d(\"\"),c=this.deferredUpdate=e.deferredCall(this.onUpdate.bind(this)),f=this;a.on(\"change\",function(a){b.applyDeltas([a.data]),c.schedule(f.$timeout)})};(function(){this.$timeout=500,this.setTimeout=function(a){this.$timeout=a},this.setValue=function(a){this.doc.setValue(a),this.deferredUpdate.schedule(this.$timeout)},this.getValue=function(a){this.sender.callback(this.doc.getValue(),a)},this.onUpdate=function(){}}).call(f.prototype)}),define(\"ace/document\",function(a,b,c){var d=a(\"pilot/oop\"),e=a(\"pilot/event_emitter\").EventEmitter,f=a(\"ace/range\").Range,g=a(\"ace/anchor\").Anchor,h=function(a){this.$lines=[],Array.isArray(a)?this.insertLines(0,a):a.length==0?this.$lines=[\"\"]:this.insert({row:0,column:0},a)};(function(){d.implement(this,e),this.setValue=function(a){var b=this.getLength();this.remove(new f(0,0,b,this.getLine(b-1).length)),this.insert({row:0,column:0},a)},this.getValue=function(){return this.getAllLines().join(this.getNewLineCharacter())},this.createAnchor=function(a,b){return new g(this,a,b)},\"aaa\".split(/a/).length==0?this.$split=function(a){return a.replace(/\\r\\n|\\r/g,\"\\n\").split(\"\\n\")}:this.$split=function(a){return a.split(/\\r\\n|\\r|\\n/)},this.$detectNewLine=function(a){var b=a.match(/^.*?(\\r?\\n)/m);b?this.$autoNewLine=b[1]:this.$autoNewLine=\"\\n\"},this.getNewLineCharacter=function(){switch(this.$newLineMode){case\"windows\":return\"\\r\\n\";case\"unix\":return\"\\n\";case\"auto\":return this.$autoNewLine}},this.$autoNewLine=\"\\n\",this.$newLineMode=\"auto\",this.setNewLineMode=function(a){this.$newLineMode!==a&&(this.$newLineMode=a)},this.getNewLineMode=function(){return this.$newLineMode},this.isNewLine=function(a){return a==\"\\r\\n\"||a==\"\\r\"||a==\"\\n\"},this.getLine=function(a){return this.getLines(a,a+1)[0]||\"\"},this.getLines=function(a,b){return this.$lines.slice(a,b+1)},this.getAllLines=function(){return this.getLines(0,this.getLength())},this.getLength=function(){return this.$lines.length},this.getTextRange=function(a){if(a.start.row==a.end.row)return this.$lines[a.start.row].substring(a.start.column,a.end.column);var b=[];b.push(this.$lines[a.start.row].substring(a.start.column)),b.push.apply(b,this.getLines(a.start.row+1,a.end.row-1)),b.push(this.$lines[a.end.row].substring(0,a.end.column));return b.join(this.getNewLineCharacter())},this.$clipPosition=function(a){var b=this.getLength();a.row>=b&&(a.row=Math.max(0,b-1),a.column=this.getLine(b-1).length);return a},this.insert=function(a,b){if(b.length==0)return a;a=this.$clipPosition(a),this.getLength()<=1&&this.$detectNewLine(b);var c=this.$split(b),d=c.splice(0,1)[0],e=c.length==0?null:c.splice(c.length-1,1)[0];a=this.insertInLine(a,d),e!==null&&(a=this.insertNewLine(a),a=this.insertLines(a.row,c),a=this.insertInLine(a,e||\"\"));return a},this.insertLines=function(a,b){if(b.length==0)return{row:a,column:0};var c=[a,0];c.push.apply(c,b),this.$lines.splice.apply(this.$lines,c);var d=new f(a,0,a+b.length,0),e={action:\"insertLines\",range:d,lines:b};this._dispatchEvent(\"change\",{data:e});return d.end},this.insertNewLine=function(a){a=this.$clipPosition(a);var b=this.$lines[a.row]||\"\";this.$lines[a.row]=b.substring(0,a.column),this.$lines.splice(a.row+1,0,b.substring(a.column,b.length));var c={row:a.row+1,column:0},d={action:\"insertText\",range:f.fromPoints(a,c),text:this.getNewLineCharacter()};this._dispatchEvent(\"change\",{data:d});return c},this.insertInLine=function(a,b){if(b.length==0)return a;var c=this.$lines[a.row]||\"\";this.$lines[a.row]=c.substring(0,a.column)+b+c.substring(a.column);var d={row:a.row,column:a.column+b.length},e={action:\"insertText\",range:f.fromPoints(a,d),text:b};this._dispatchEvent(\"change\",{data:e});return d},this.remove=function(a){a.start=this.$clipPosition(a.start),a.end=this.$clipPosition(a.end);if(a.isEmpty())return a.start;var b=a.start.row,c=a.end.row;if(a.isMultiLine()){var d=a.start.column==0?b:b+1,e=c-1;a.end.column>0&&this.removeInLine(c,0,a.end.column),e>=d&&this.removeLines(d,e),d!=b&&(this.removeInLine(b,a.start.column,this.getLine(b).length),this.removeNewLine(a.start.row))}else this.removeInLine(b,a.start.column,a.end.column);return a.start},this.removeInLine=function(a,b,c){if(b!=c){var d=new f(a,b,a,c),e=this.getLine(a),g=e.substring(b,c),h=e.substring(0,b)+e.substring(c,e.length);this.$lines.splice(a,1,h);var i={action:\"removeText\",range:d,text:g};this._dispatchEvent(\"change\",{data:i});return d.start}},this.removeLines=function(a,b){var c=new f(a,0,b+1,0),d=this.$lines.splice(a,b-a+1),e={action:\"removeLines\",range:c,nl:this.getNewLineCharacter(),lines:d};this._dispatchEvent(\"change\",{data:e});return d},this.removeNewLine=function(a){var b=this.getLine(a),c=this.getLine(a+1),d=new f(a,b.length,a+1,0),e=b+c;this.$lines.splice(a,2,e);var g={action:\"removeText\",range:d,text:this.getNewLineCharacter()};this._dispatchEvent(\"change\",{data:g})},this.replace=function(a,b){if(b.length==0&&a.isEmpty())return a.start;if(b==this.getTextRange(a))return a.end;this.remove(a);if(b)var c=this.insert(a.start,b);else c=a.start;return c},this.applyDeltas=function(a){for(var b=0;b<a.length;b++){var c=a[b],d=f.fromPoints(c.range.start,c.range.end);c.action==\"insertLines\"?this.insertLines(d.start.row,c.lines):c.action==\"insertText\"?this.insert(d.start,c.text):c.action==\"removeLines\"?this.removeLines(d.start.row,d.end.row-1):c.action==\"removeText\"&&this.remove(d)}},this.revertDeltas=function(a){for(var b=a.length-1;b>=0;b--){var c=a[b],d=f.fromPoints(c.range.start,c.range.end);c.action==\"insertLines\"?this.removeLines(d.start.row,d.end.row-1):c.action==\"insertText\"?this.remove(d):c.action==\"removeLines\"?this.insertLines(d.start.row,c.lines):c.action==\"removeText\"&&this.insert(d.start,c.text)}}}).call(h.prototype),b.Document=h}),define(\"ace/range\",function(a,b,c){var d=function(a,b,c,d){this.start={row:a,column:b},this.end={row:c,column:d}};(function(){this.toString=function(){return\"Range: [\"+this.start.row+\"/\"+this.start.column+\"] -> [\"+this.end.row+\"/\"+this.end.column+\"]\"},this.contains=function(a,b){return this.compare(a,b)==0},this.compare=function(a,b){if(!this.isMultiLine()&&a===this.start.row)return b<this.start.column?-1:b>this.end.column?1:0;if(a<this.start.row)return-1;if(a>this.end.row)return 1;if(this.start.row===a)return b>=this.start.column?0:-1;if(this.end.row===a)return b<=this.end.column?0:1;return 0},this.clipRows=function(a,b){if(this.end.row>b)var c={row:b+1,column:0};if(this.start.row>b)var e={row:b+1,column:0};if(this.start.row<a)var e={row:a,column:0};if(this.end.row<a)var c={row:a,column:0};return d.fromPoints(e||this.start,c||this.end)},this.extend=function(a,b){var c=this.compare(a,b);if(c==0)return this;if(c==-1)var e={row:a,column:b};else var f={row:a,column:b};return d.fromPoints(e||this.start,f||this.end)},this.isEmpty=function(){return this.start.row==this.end.row&&this.start.column==this.end.column},this.isMultiLine=function(){return this.start.row!==this.end.row},this.clone=function(){return d.fromPoints(this.start,this.end)},this.collapseRows=function(){return this.end.column==0?new d(this.start.row,0,Math.max(this.start.row,this.end.row-1),0):new d(this.start.row,0,this.end.row,0)},this.toScreenRange=function(a){var b=a.documentToScreenPosition(this.start),c=a.documentToScreenPosition(this.end);return new d(b.row,b.column,c.row,c.column)}}).call(d.prototype),d.fromPoints=function(a,b){return new d(a.row,a.column,b.row,b.column)},b.Range=d}),define(\"ace/anchor\",function(a,b,c){var d=a(\"pilot/oop\"),e=a(\"pilot/event_emitter\").EventEmitter,f=b.Anchor=function(a,b,c){this.document=a,typeof c==\"undefined\"?this.setPosition(b.row,b.column):this.setPosition(b,c),this.$onChange=this.onChange.bind(this),a.on(\"change\",this.$onChange)};(function(){d.implement(this,e),this.getPosition=function(){return this.$clipPositionToDocument(this.row,this.column)},this.getDocument=function(){return this.document},this.onChange=function(a){var b=a.data,c=b.range;if(c.start.row!=c.end.row||c.start.row==this.row){if(c.start.row>this.row)return;if(c.start.row==this.row&&c.start.column>this.column)return;var d=this.row,e=this.column;b.action===\"insertText\"?c.start.row===d&&c.start.column<=e?c.start.row===c.end.row?e+=c.end.column-c.start.column:(e-=c.start.column,d+=c.end.row-c.start.row):c.start.row!==c.end.row&&c.start.row<d&&(d+=c.end.row-c.start.row):b.action===\"insertLines\"?c.start.row<=d&&(d+=c.end.row-c.start.row):b.action==\"removeText\"?c.start.row==d&&c.start.column<e?c.end.column>=e?e=c.start.column:e=Math.max(0,e-(c.end.column-c.start.column)):c.start.row!==c.end.row&&c.start.row<d?(c.end.row==d&&(e=Math.max(0,e-c.end.column)+c.start.column),d-=c.end.row-c.start.row):c.end.row==d&&(d-=c.end.row-c.start.row,e=Math.max(0,e-c.end.column)+c.start.column):b.action==\"removeLines\"&&c.start.row<=d&&(c.end.row<=d?d-=c.end.row-c.start.row:(d=c.start.row,e=0)),this.setPosition(d,e,!0)}},this.setPosition=function(a,b,c){c?pos={row:a,column:b}:pos=this.$clipPositionToDocument(a,b);if(this.row!=pos.row||this.column!=pos.column){var d={row:this.row,column:this.column};this.row=pos.row,this.column=pos.column,this._dispatchEvent(\"change\",{old:d,value:pos})}},this.detach=function(){this.document.removeEventListener(\"change\",this.$onChange)},this.$clipPositionToDocument=function(a,b){var c={};a>=this.document.getLength()?(c.row=Math.max(0,this.document.getLength()-1),c.column=this.document.getLine(c.row).length):a<0?(c.row=0,c.column=0):(c.row=a,c.column=Math.min(this.document.getLine(c.row).length,Math.max(0,b))),b<0&&(c.column=0);return c}}).call(f.prototype)}),define(\"pilot/lang\",function(a,b,c){b.stringReverse=function(a){return a.split(\"\").reverse().join(\"\")},b.stringRepeat=function(a,b){return Array(b+1).join(a)},b.copyObject=function(a){var b={};for(var c in a)b[c]=a[c];return b},b.arrayToMap=function(a){var b={};for(var c=0;c<a.length;c++)b[a[c]]=1;return b},b.arrayRemove=function(a,b){for(var c=0;c<=a.length;c++)b===a[c]&&a.splice(c,1)},b.escapeRegExp=function(a){return a.replace(/([.*+?^${}()|[\\]\\/\\\\])/g,\"\\\\$1\")},b.deferredCall=function(a){var b=null,c=function(){b=null,a()},d=function(a){b||(b=setTimeout(c,a||0));return d};d.schedule=d,d.call=function(){this.cancel(),a();return d},d.cancel=function(){clearTimeout(b),b=null;return d};return d}}),define(\"ace/worker/jshint\",function(a,b,c){var d=function(){function cT(){function b(){var a=K;bI(\"[\");if(K.id!==\"]\")for(;;){if(K.id===\"(end)\")bC(\"Missing ']' to match '[' from line {a}.\",K,a.line);else{if(K.id===\"]\"){bA(\"Unexpected comma.\",$);break}K.id===\",\"&&bC(\"Unexpected comma.\",K)}cT();if(K.id!==\",\")break;bI(\",\")}bI(\"]\")}function a(){var a={},b=K;bI(\"{\");if(K.id!==\"}\")for(;;){if(K.id===\"(end)\")bC(\"Missing '}' to match '{' from line {a}.\",K,b.line);else{if(K.id===\"}\"){bA(\"Unexpected comma.\",$);break}K.id===\",\"?bC(\"Unexpected comma.\",K):K.id!==\"(string)\"&&bA(\"Expected a string and instead saw {a}.\",K,K.value)}a[K.value]===!0?bA(\"Duplicate key '{a}'.\",K,K.value):K.value===\"__proto__\"?bA(\"Stupid key '{a}'.\",K,K.value):a[K.value]=!0,bI(),bI(\":\"),cT();if(K.id!==\",\")break;bI(\",\")}bI(\"}\")}switch(K.id){case\"{\":a();break;case\"[\":b();break;case\"true\":case\"false\":case\"null\":case\"(number)\":case\"(string)\":bI();break;case\"-\":bI(\"-\"),$.character!==K.from&&bA(\"Unexpected space after '-'.\",$),bK($,K),bI(\"(number)\");break;default:bC(\"Expected a JSON value.\",K)}}function cR(a,b){var c,d=S;S=Object.create(d),v={\"(name)\":a||'\"'+e+'\"',\"(line)\":K.line,\"(context)\":v,\"(breakage)\":0,\"(loopage)\":0,\"(scope)\":S,\"(statement)\":b},c=v,$.funct=v,x.push(v),a&&bF(a,\"function\"),v[\"(params)\"]=cQ(),cm(!1),S=d,v[\"(last)\"]=$.line,v=v[\"(context)\"];return c}function cQ(){var a,b=K,c=[];bI(\"(\"),bM();if(K.id===\")\")bI(\")\"),bM(Q,$);else for(;;){a=ch(!0),c.push(a),bF(a,\"parameter\");if(K.id===\",\")bR();else{bI(\")\",b),bM(Q,$);return c}}}function cP(){var a=cg(!0);a||(K.id===\"(string)\"?(a=K.value,N.adsafe&&(a.charAt(0)===\"_\"||a.charAt(a.length-1)===\"_\")&&bA(\"Unexpected {a} in '{b}'.\",$,\"dangling '_'\",a),bI()):K.id===\"(number)\"&&(a=K.value.toString(),bI()));return a}function cO(){var a,b,c,d,e,f,g,h=N.white,i;bd=\"html\",be=\"\",U=null;for(;;){switch(K.value){case\"<\":bd=\"html\",bI(\"<\"),b={},f=K,f.identifier||bA(\"Bad identifier {a}.\",f,f.value),d=f.value,N.cap&&(d=d.toLowerCase()),f.name=d,bI(),U||(U=[],cK(d)),g=z[d],typeof g!==\"object\"&&bC(\"Unrecognized tag '<{a}>'.\",f,d),c=g.empty,f.type=d;for(;;){if(K.id===\"/\"){bI(\"/\"),K.id!==\">\"&&bA(\"Expected '{a}' and instead saw '{b}'.\",K,\">\",K.value);break}if(K.id&&K.id.substr(0,1)===\">\")break;K.identifier||((K.id===\"(end)\"||K.id===\"(error)\")&&bC(\"Missing '>'.\",K),bA(\"Bad identifier.\")),N.white=!0,bN($,K),a=K.value,N.white=h,bI(),!N.cap&&a!==a.toLowerCase()&&bA(\"Attribute '{a}' not all lower case.\",K,a),a=a.toLowerCase(),be=\"\",bw(b,a)&&bA(\"Attribute '{a}' repeated.\",K,a),a.slice(0,2)===\"on\"?(N.on||bA(\"Avoid HTML event handlers.\"),bd=\"scriptstring\",bI(\"=\"),e=K.id,e!=='\"'&&e!==\"'\"&&bC(\"Missing quote.\"),be=e,i=N.white,N.white=!1,bI(e),ck(),cl(\"on\"),N.white=i,K.id!==e&&bC(\"Missing close quote on script attribute.\"),bd=\"html\",be=\"\",bI(e),g=!1):a===\"style\"?(bd=\"scriptstring\",bI(\"=\"),e=K.id,e!=='\"'&&e!==\"'\"&&bC(\"Missing quote.\"),bd=\"styleproperty\",be=e,bI(e),cF(),bd=\"html\",be=\"\",bI(e),g=!1):K.id===\"=\"?(bI(\"=\"),g=K.value,!K.identifier&&K.id!=='\"'&&K.id!==\"'\"&&K.type!==\"(string)\"&&K.type!==\"(number)\"&&K.type!==\"(color)\"&&bA(\"Expected an attribute value and instead saw '{a}'.\",$,a),bI()):g=!0,b[a]=g,cL(d,a,g)}cM(d,b),c||U.push(f),bd=\"outer\",bI(\">\");break;case\"</\":bd=\"html\",bI(\"</\"),K.identifier||bA(\"Bad identifier.\"),d=K.value,N.cap&&(d=d.toLowerCase()),bI(),U||bC(\"Unexpected '{a}'.\",K,cN(d)),f=U.pop(),f||bC(\"Unexpected '{a}'.\",K,cN(d)),f.name!==d&&bC(\"Expected '{a}' and instead saw '{b}'.\",K,cN(f.name),cN(d)),K.id!==\">\"&&bC(\"Missing '{a}'.\",K,\">\"),bd=\"outer\",bI(\">\");break;case\"<!\":N.safe&&bA(\"ADsafe HTML violation.\"),bd=\"html\";for(;;){bI();if(K.id===\">\"||K.id===\"(end)\")break;K.value.indexOf(\"--\")>=0&&bC(\"Unexpected --.\"),K.value.indexOf(\"<\")>=0&&bC(\"Unexpected <.\"),K.value.indexOf(\">\")>=0&&bC(\"Unexpected >.\")}bd=\"outer\",bI(\">\");break;case\"(end)\":return;default:K.id===\"(end)\"?bC(\"Missing '{a}'.\",K,\"</\"+U[U.length-1].value+\">\"):bI()}if(U&&U.length===0&&(N.adsafe||!N.fragment||K.id===\"(end)\"))break}K.id!==\"(end)\"&&bC(\"Unexpected material after the end.\")}function cN(a){return\"</\"+a+\">\"}function cM(d,e){var g,h=z[d],i;T=!1,h||bC(\"Unrecognized tag '<{a}>'.\",K,d===d.toLowerCase()?d:d+\" (capitalization error)\");if(U.length>0){d===\"html\"&&bC(\"Too many <html> tags.\",$),i=h.parent;if(i)i.indexOf(\" \"+U[U.length-1].name+\" \")<0&&bC(\"A '<{a}>' must be within '<{b}>'.\",$,d,i);else if(!N.adsafe&&!N.fragment){g=U.length;do g<=0&&bC(\"A '<{a}>' must be within '<{b}>'.\",$,d,\"body\"),g-=1;while(U[g].name!==\"body\")}}switch(d){case\"div\":N.adsafe&&U.length===1&&!a&&bA(\"ADSAFE violation: missing ID_.\");break;case\"script\":bd=\"script\",bI(\">\"),D=K.from,e.lang&&bA(\"lang is deprecated.\",$),N.adsafe&&U.length!==1&&bA(\"ADsafe script placement violation.\",$),e.src?(N.adsafe&&(!b||!f[e.src])&&bA(\"ADsafe unapproved script source.\",$),e.type&&bA(\"type is unnecessary.\",$)):(c&&bC(\"ADsafe script violation.\",$),ck(),cl(\"script\")),bd=\"html\",bI(\"</\"),!K.identifier&&K.value!==\"script\"&&bA(\"Expected '{a}' and instead saw '{b}'.\",K,\"script\",K.value),bI(),bd=\"outer\";break;case\"style\":bd=\"style\",bI(\">\"),cJ(),bd=\"html\",bI(\"</\"),!K.identifier&&K.value!==\"style\"&&bA(\"Expected '{a}' and instead saw '{b}'.\",K,\"style\",K.value),bI(),bd=\"outer\";break;case\"input\":switch(e.type){case\"radio\":case\"checkbox\":case\"button\":case\"reset\":case\"submit\":break;case\"text\":case\"file\":case\"password\":case\"file\":case\"hidden\":case\"image\":N.adsafe&&e.autocomplete!==\"off\"&&bA(\"ADsafe autocomplete violation.\");break;default:bA(\"Bad input type.\")}break;case\"applet\":case\"body\":case\"embed\":case\"frame\":case\"frameset\":case\"head\":case\"iframe\":case\"noembed\":case\"noframes\":case\"object\":case\"param\":N.adsafe&&bA(\"ADsafe violation: Disallowed tag: \"+d)}}function cL(b,c,d){var e,f;c===\"id\"?(e=typeof d===\"string\"?d.toUpperCase():\"\",A[e]===!0&&bA(\"Duplicate id='{a}'.\",K,d),/^[A-Za-z][A-Za-z0-9._:\\-]*$/.test(d)?N.adsafe&&(a?d.slice(0,a.length)!==a?bA(\"ADsafe violation: An id must have a '{a}' prefix\",K,a):/^[A-Z]+_[A-Z]+$/.test(d)||bA(\"ADSAFE violation: bad id.\"):(a=d,/^[A-Z]+_$/.test(d)||bA(\"ADSAFE violation: bad id.\"))):bA(\"Bad id: '{a}'.\",K,d),f=d.search(bt),f>=0&&bA(\"Unexpected character '{a}' in {b}.\",$,d.charAt(f),c),A[e]=!0):c===\"class\"||c===\"type\"||c===\"name\"?(f=d.search(bs),f>=0&&bA(\"Unexpected character '{a}' in {b}.\",$,d.charAt(f),c),A[e]=!0):c===\"href\"||c===\"background\"||c===\"content\"||c===\"data\"||c.indexOf(\"src\")>=0||c.indexOf(\"url\")>=0?(N.safe&&bp.test(d)&&bC(\"ADsafe URL violation.\"),_.push(d)):c===\"for\"?N.adsafe&&(a?d.slice(0,a.length)!==a?bA(\"ADsafe violation: An id must have a '{a}' prefix\",K,a):/^[A-Z]+_[A-Z]+$/.test(d)||bA(\"ADSAFE violation: bad id.\"):bA(\"ADSAFE violation: bad id.\")):c===\"name\"&&N.adsafe&&d.indexOf(\"_\")>=0&&bA(\"ADsafe name violation.\")}function cK(a){a!==\"html\"&&!N.fragment&&(a===\"div\"&&N.adsafe?bC(\"ADSAFE: Use the fragment option.\"):bC(\"Expected '{a}' and instead saw '{b}'.\",$,\"html\",a)),N.adsafe&&(a===\"html\"&&bC(\"Currently, ADsafe does not operate on whole HTML documents. It operates on <div> fragments and .js files.\",$),N.fragment?a!==\"div\"&&bC(\"ADsafe violation: Wrap the widget in a div.\",$):bC(\"Use the fragment option.\",$)),N.browser=!0,by()}function cJ(){var a;while(K.id===\"@\"){a=bH(),bI(\"@\");if(K.identifier)switch(K.value){case\"import\":bI(),cB()||(bA(\"Expected '{a}' and instead saw '{b}'.\",K,\"url\",K.value),bI()),bI(\";\");break;case\"media\":bI();for(;;){(!K.identifier||r[K.value]===!0)&&bC(\"Expected a CSS media type, and instead saw '{a}'.\",K,K.id),bI();if(K.id!==\",\")break;bI(\",\")}bI(\"{\"),cI(),bI(\"}\");break;default:bA(\"Expected an at-rule, and instead saw @{a}.\",K,K.value)}else bA(\"Expected an at-rule, and instead saw '{a}'.\",K,K.value)}cI()}function cI(){while(K.id!==\"</\"&&K.id!==\"(end)\")cH(),bd=\"styleproperty\",K.id===\";\"?bI(\";\"):(bI(\"{\"),cF(),bd=\"style\",bI(\"}\"))}function cH(){K.id===\"{\"&&bA(\"Expected a style pattern, and instead saw '{a}'.\",K,K.id);for(;;){cG();if(K.id===\"</\"||K.id===\"{\"||K.id===\"(end)\")return\"\";K.id===\",\"&&bR()}}function cG(){if(K.identifier)bw(z,N.cap?K.value.toLowerCase():K.value)||bA(\"Expected a tagName, and instead saw {a}.\",K,K.value),bI();else switch(K.id){case\">\":case\"+\":bI(),cG();break;case\":\":bI(\":\");switch(K.value){case\"active\":case\"after\":case\"before\":case\"checked\":case\"disabled\":case\"empty\":case\"enabled\":case\"first-child\":case\"first-letter\":case\"first-line\":case\"first-of-type\":case\"focus\":case\"hover\":case\"last-child\":case\"last-of-type\":case\"link\":case\"only-of-type\":case\"root\":case\"target\":case\"visited\":bI();break;case\"lang\":bI(),bI(\"(\"),K.identifier||bA(\"Expected a lang code, and instead saw :{a}.\",K,K.value),bI(\")\");break;case\"nth-child\":case\"nth-last-child\":case\"nth-last-of-type\":case\"nth-of-type\":bI(),bI(\"(\"),cE(),bI(\")\");break;case\"not\":bI(),bI(\"(\"),K.id===\":\"&&bH(0).value===\"not\"&&bA(\"Nested not.\"),cG(),bI(\")\");break;default:bA(\"Expected a pseudo, and instead saw :{a}.\",K,K.value)}break;case\"#\":bI(\"#\"),K.identifier||bA(\"Expected an id, and instead saw #{a}.\",K,K.value),bI();break;case\"*\":bI(\"*\");break;case\".\":bI(\".\"),K.identifier||bA(\"Expected a class, and instead saw #.{a}.\",K,K.value),bI();break;case\"[\":bI(\"[\"),K.identifier||bA(\"Expected an attribute, and instead saw [{a}].\",K,K.value),bI();if(K.id===\"=\"||K.value===\"~=\"||K.value===\"$=\"||K.value===\"|=\"||K.id===\"*=\"||K.id===\"^=\")bI(),K.type!==\"(string)\"&&bA(\"Expected a string, and instead saw {a}.\",K,K.value),bI();bI(\"]\");break;default:bC(\"Expected a CSS selector, and instead saw {a}.\",K,K.value)}}function cF(){var a;for(;;){if(K.id===\"}\"||K.id===\"(end)\"||be&&K.id===be)return;while(K.id===\";\")bA(\"Misplaced ';'.\"),bI(\";\");a=cC(),bI(\":\"),K.identifier&&K.value===\"inherit\"?bI():cD(a)||(bA(\"Unexpected token '{a}'.\",K,K.value),bI()),K.id===\"!\"&&(bI(\"!\"),bK(),K.identifier&&K.value===\"important\"?bI():bA(\"Expected '{a}' and instead saw '{b}'.\",K,\"important\",K.value)),K.id===\"}\"||K.id===be?bA(\"Missing '{a}'.\",K,\";\"):bI(\";\")}}function cE(){if(K.id===\"(number)\")bI(),K.value===\"n\"&&K.identifier&&(bK(),bI(),K.id===\"+\"&&(bK(),bI(\"+\"),bK(),bI(\"(number)\")));else{switch(K.value){case\"odd\":case\"even\":if(K.identifier){bI();return}}bA(\"Unexpected token '{a}'.\",K,K.value)}}function cD(a){var b=0,c,d,e,f,g=0,h;switch(typeof a){case\"function\":return a();case\"string\":if(K.identifier&&K.value===a){bI();return!0}return!1}for(;;){if(b>=a.length)return!1;h=a[b],b+=1;if(h===!0)break;typeof h===\"number\"?(c=h,h=a[b],b+=1):c=1,e=!1;while(c>0)if(cD(h))e=!0,c-=1;else break;if(e)return!0}g=b,d=[];for(;;){f=!1;for(b=g;b<a.length;b+=1)if(!d[b]&&cD(l[a[b]])){e=!0,f=!0,d[b]=!0;break}if(!f)return e}}function cC(){var a;while(K.id===\"*\"||K.id===\"#\"||K.value===\"_\")N.css||bA(\"Unexpected '{a}'.\",K,K.value),bI();if(K.id===\"-\"){N.css||bA(\"Unexpected '{a}'.\",K,K.value),bI(\"-\"),K.identifier||bA(\"Expected a non-standard style attribute and instead saw '{a}'.\",K,K.value),bI();return m}K.identifier?bw(l,K.value)?a=l[K.value]:(a=m,N.css||bA(\"Unrecognized style attribute '{a}'.\",K,K.value)):bA(\"Excepted a style attribute, and instead saw '{a}'.\",K,K.value),bI();return a}function cB(){var a,b;if(K.identifier&&K.value===\"url\"){K=bE.range(\"(\",\")\"),b=K.value,a=b.charAt(0);if(a==='\"'||a===\"'\")b.slice(-1)!==a?bA(\"Bad url string.\"):(b=b.slice(1,-1),b.indexOf(a)>=0&&bA(\"Bad url string.\"));b||bA(\"Missing url.\"),bI(),N.safe&&bp.test(b)&&bC(\"ADsafe URL violation.\"),_.push(b);return!0}return!1}function cA(){var a;if(K.identifier&&K.value===\"rect\"){bI(),bI(\"(\");for(a=0;a<4;a+=1)if(!ct()){bA(\"Expected a number and instead saw '{a}'.\",K,K.value);break}bI(\")\");return!0}return!1}function cz(){if(K.identifier&&K.value===\"counter\"){bI(),bI(\"(\"),bI(),K.id===\",\"&&(bR(),K.type!==\"(string)\"&&bA(\"Expected a string and instead saw '{a}'.\",K,K.value),bI()),bI(\")\");return!0}if(K.identifier&&K.value===\"counters\"){bI(),bI(\"(\"),K.identifier||bA(\"Expected a name and instead saw '{a}'.\",K,K.value),bI(),K.id===\",\"&&(bR(),K.type!==\"(string)\"&&bA(\"Expected a string and instead saw '{a}'.\",K,K.value),bI()),K.id===\",\"&&(bR(),K.type!==\"(string)\"&&bA(\"Expected a string and instead saw '{a}'.\",K,K.value),bI()),bI(\")\");return!0}return!1}function cy(){while(K.id!==\";\"){!cp()&&!cr()&&bA(\"Expected a name and instead saw '{a}'.\",K,K.value);if(K.id!==\",\")return!0;bR()}}function cx(){if(K.identifier&&K.value===\"attr\"){bI(),bI(\"(\"),K.identifier||bA(\"Expected a name and instead saw '{a}'.\",K,K.value),bI(),bI(\")\");return!0}return!1}function cw(){if(!K.identifier)return ct();if(K.value===\"auto\"){bI();return!0}}function cv(){if(!K.identifier)return ct();switch(K.value){case\"thin\":case\"medium\":case\"thick\":bI();return!0}}function cu(){K.id===\"-\"&&(bI(\"-\"),bK());if(K.type===\"(number)\"){bI(),K.type!==\"(string)\"&&q[K.value]===!0&&(bK(),bI());return!0}return!1}function ct(){K.id===\"-\"&&(bI(\"-\"),bK(),bQ());if(K.type===\"(number)\"){bI(),K.type!==\"(string)\"&&q[K.value]===!0?(bK(),bI()):+$.value!==0&&bA(\"Expected a linear unit and instead saw '{a}'.\",K,K.value);return!0}return!1}function cs(){var a,b,c;if(K.identifier){c=K.value;if(c===\"rgb\"||c===\"rgba\"){bI(),bI(\"(\");for(a=0;a<3;a+=1)a&&bI(\",\"),b=K.value,K.type!==\"(number)\"||b<0?(bA(\"Expected a positive number and instead saw '{a}'\",K,b),bI()):(bI(),K.id===\"%\"?(bI(\"%\"),b>100&&bA(\"Expected a percentage and instead saw '{a}'\",$,b)):b>255&&bA(\"Expected a small number and instead saw '{a}'\",$,b));c===\"rgba\"&&(bI(\",\"),b=+K.value,(K.type!==\"(number)\"||b<0||b>1)&&bA(\"Expected a number between 0 and 1 and instead saw '{a}'\",K,b),bI(),K.id===\"%\"&&(bA(\"Unexpected '%'.\"),bI(\"%\"))),bI(\")\");return!0}if(n[K.value]===!0){bI();return!0}}else if(K.type===\"(color)\"){bI();return!0}return!1}function cr(){if(K.type===\"(string)\"){bI();return!0}}function cq(){K.id===\"-\"&&(bI(\"-\"),bK(),bQ());if(K.type===\"(number)\"){bI(\"(number)\");return!0}}function cp(){if(K.identifier){bI();return!0}}function co(a){var b=a.value,c=a.line,d=B[b];typeof d===\"function\"&&(d=!1),d?d[d.length-1]!==c&&d.push(c):(d=[c],B[b]=d)}function cn(a){J&&typeof J[a]!==\"boolean\"&&bA(\"Unexpected /*member '{a}'.\",$,a),typeof I[a]===\"number\"?I[a]+=1:I[a]=1}function cm(a,b){var c,d=C,e=D,f=X,g=S,h;C=a,S=Object.create(S),bN($,K),h=K;if(K.id===\"{\"){bI(\"{\");if(K.id!==\"}\"||$.line!==K.line){D+=N.indent;while(!a&&K.from>D)D+=N.indent;!a&&!ck()&&!f&&N.strict&&v[\"(context)\"][\"(global)\"]&&bA('Missing \"use strict\" statement.'),c=cl(),X=f,D-=N.indent,bP()}bI(\"}\",h),D=e}else a?((!b||N.curly)&&bA(\"Expected '{a}' and instead saw '{b}'.\",K,\"{\",K.value),M=!0,c=[cj()],M=!1):bC(\"Expected '{a}' and instead saw '{b}'.\",K,\"{\",K.value);v[\"(verb)\"]=null,S=g,C=d,a&&N.noempty&&(!c||c.length===0)&&bA(\"Empty block.\");return c}function cl(c){var d=[],e,f;if(N.adsafe)switch(c){case\"script\":b||(K.value!==\"ADSAFE\"||bH(0).id!==\".\"||bH(1).value!==\"id\"&&bH(1).value!==\"go\")&&bC(\"ADsafe violation: Missing ADSAFE.id or ADSAFE.go.\",K),K.value===\"ADSAFE\"&&bH(0).id===\".\"&&bH(1).value===\"id\"&&(b&&bC(\"ADsafe violation.\",K),bI(\"ADSAFE\"),bI(\".\"),bI(\"id\"),bI(\"(\"),K.value!==a&&bC(\"ADsafe violation: id does not match.\",K),bI(\"(string)\"),bI(\")\"),bI(\";\"),b=!0);break;case\"lib\":if(K.value===\"ADSAFE\"){bI(\"ADSAFE\"),bI(\".\"),bI(\"lib\"),bI(\"(\"),bI(\"(string)\"),bR(),e=bJ(0),e.id!==\"function\"&&bC(\"The second argument to lib must be a function.\",e),f=e.funct[\"(params)\"],f=f&&f.join(\", \"),f&&f!==\"lib\"&&bC(\"Expected '{a}' and instead saw '{b}'.\",e,\"(lib)\",\"(\"+f+\")\"),bI(\")\"),bI(\";\");return d}bC(\"ADsafe lib violation.\")}while(!K.reach&&K.id!==\"(end)\")K.id===\";\"?(bA(\"Unnecessary semicolon.\"),bI(\";\")):d.push(cj());return d}function ck(){if(K.value===\"use strict\"){X&&bA('Unnecessary \"use strict\".'),bI(),bI(\";\"),X=!0,N.newcap=!0,N.undef=!0;return!0}return!1}function cj(a){var b=D,c,d=S,e=K;if(e.id===\";\")bA(\"Unnecessary semicolon.\",e),bI(\";\");else{e.identifier&&!e.reserved&&bH().id===\":\"&&(bI(),bI(\":\"),S=Object.create(d),bF(e.value,\"label\"),K.labelled||bA(\"Label '{a}' on {b} statement.\",K,e.value,K.value),bo.test(e.value+\":\")&&bA(\"Label '{a}' looks like a javascript url.\",e,e.value),K.label=e.value,e=K),a||bP(),c=bJ(0,!0),e.block||(!c||!c.exps?bA(\"Expected an assignment or function call and instead saw an expression.\",$):N.nonew&&c.id===\"(\"&&c.left.id===\"new\"&&bA(\"Do not use 'new' for side effects.\"),K.id!==\";\"?bB(\"Missing semicolon.\",$.line,$.from+$.value.length):(bK($,K),bI(\";\"),bN($,K))),D=b,S=d;return c}}function ci(a){var b=0,c;if(K.id===\";\"&&!M)for(;;){c=bH(b);if(c.reach)return;if(c.id!==\"(endline)\"){if(c.id===\"function\"){bA(\"Inner functions should be listed at the top of the outer function.\",c);break}bA(\"Unreachable '{a}' after '{b}'.\",c,c.value,a);break}b+=1}}function ch(a){var b=cg(a);if(b)return b;$.id===\"function\"&&K.id===\"(\"?bA(\"Missing name in function statement.\"):bC(\"Expected an identifier and instead saw '{a}'.\",K,K.value)}function cg(a){if(K.identifier){bI(),N.safe&&h[$.value]?bA(\"ADsafe violation: '{a}'.\",$,$.value):$.reserved&&!N.es5&&(!a||$.value!=\"undefined\")&&bA(\"Expected an identifier and instead saw '{a}' (a reserved word).\",$,$.id);return $.value}}function cf(a,b){var c=bS(a,150);c.led=function(a){N.plusplus?bA(\"Unexpected use of '{a}'.\",this,this.id):(!a.identifier||a.reserved)&&a.id!==\".\"&&a.id!==\"[\"&&bA(\"Bad operand.\",this),this.left=a;return this};return c}function ce(a){bS(a,20).exps=!0;return b_(a,function(a,b){N.bitwise&&bA(\"Unexpected use of '{a}'.\",b,b.id),bN(Q,$),bN($,K);if(a){if(a.id===\".\"||a.id===\"[\"||a.identifier&&!a.reserved){bJ(19);return b}a===Y[\"function\"]&&bA(\"Expected an identifier in an assignment, and instead saw a function invocation.\",$);return b}bC(\"Bad assignment.\",b)},20)}function cd(a,b,c){var d=bS(a,c);bW(d),d.led=typeof b===\"function\"?b:function(a){N.bitwise&&bA(\"Unexpected use of '{a}'.\",this,this.id),this.left=a,this.right=bJ(c);return this};return d}function cc(a,b){bS(a,20).exps=!0;return b_(a,function(a,b){var c;b.left=a,O[a.value]===!1&&S[a.value][\"(global)\"]===!0?bA(\"Read only.\",a):a[\"function\"]&&bA(\"'{a}' is a function.\",a,a.value);if(N.safe){c=a;do typeof O[c.value]===\"boolean\"&&bA(\"ADsafe violation.\",c),c=c.left;while(c)}if(a){if(a.id===\".\"||a.id===\"[\"){(!a.left||a.left.value===\"arguments\")&&bA(\"Bad assignment.\",b),b.right=bJ(19);return b}if(a.identifier&&!a.reserved){v[a.value]===\"exception\"&&bA(\"Do not assign to the exception parameter.\",a),b.right=bJ(19);return b}a===Y[\"function\"]&&bA(\"Expected an identifier in an assignment and instead saw a function invocation.\",$)}bC(\"Bad assignment.\",b)},20)}function cb(a){return a&&(a.type===\"(number)\"&&+a.value===0||a.type===\"(string)\"&&a.value===\"\"||a.type===\"null\"&&!N.boss||a.type===\"true\"||a.type===\"false\"||a.type===\"undefined\")}function ca(a,b){var c=bS(a,100);c.led=function(a){bO(Q,$),bN($,K);var c=bJ(100);a&&a.id===\"NaN\"||c&&c.id===\"NaN\"?bA(\"Use the isNaN function to compare with NaN.\",this):b&&b.apply(this,[a,c]),a.id===\"!\"&&bA(\"Confusing use of '{a}'.\",a,\"!\"),c.id===\"!\"&&bA(\"Confusing use of '{a}'.\",a,\"!\"),this.left=a,this.right=c;return this};return c}function b_(a,b,c,d){var e=bS(a,c);bW(e),e.led=function(a){d||(bO(Q,$),bN($,K));if(typeof b===\"function\")return b(a,this);this.left=a,this.right=bJ(c);return this};return e}function b$(a,b){return bZ(a,function(){typeof b===\"function\"&&b(this);return this})}function bZ(a,b){var c=bY(a,b);c.identifier=c.reserved=!0;return c}function bY(a,b){var c=bT(a);c.type=a,c.nud=b;return c}function bX(a,b){var c=bS(a,150);bW(c),c.nud=typeof b===\"function\"?b:function(){this.right=bJ(150),this.arity=\"unary\";if(this.id===\"++\"||this.id===\"--\")N.plusplus?bA(\"Unexpected use of '{a}'.\",this,this.id):(!this.right.identifier||this.right.reserved)&&this.right.id!==\".\"&&this.right.id!==\"[\"&&bA(\"Bad operand.\",this);return this};return c}function bW(a){var b=a.id.charAt(0);if(b>=\"a\"&&b<=\"z\"||b>=\"A\"&&b<=\"Z\")a.identifier=a.reserved=!0;return a}function bV(a,b){var c=bU(a,b);c.block=!0;return c}function bU(a,b){var c=bT(a);c.identifier=c.reserved=!0,c.fud=b;return c}function bT(a){return bS(a,0)}function bS(a,b){var c=Y[a];if(!c||typeof c!==\"object\")Y[a]=c={id:a,lbp:b,value:a};return c}function bR(){$.line!==K.line?N.laxbreak||bA(\"Bad line breaking before '{a}'.\",$,K.id):$.character!==K.from&&N.white&&bA(\"Unexpected space after '{a}'.\",K,$.value),bI(\",\"),bN($,K)}function bQ(a){a=a||$,a.line!==K.line&&bA(\"Line breaking error '{a}'.\",a,a.value)}function bP(a){var b;N.white&&K.id!==\"(end)\"&&(b=D+(a||0),K.from!==b&&bA(\"Expected '{a}' to have an indentation at {b} instead at {c}.\",K,K.value,b,K.from))}function bO(a,b){a=a||$,b=b||K,!N.laxbreak&&a.line!==b.line?bA(\"Bad line breaking before '{a}'.\",b,b.id):N.white&&(a=a||$,b=b||K,a.character===b.from&&bA(\"Missing space after '{a}'.\",K,a.value))}function bN(a,b){N.white&&(a=a||$,b=b||K,a.line===b.line&&a.character===b.from&&bA(\"Missing space after '{a}'.\",K,a.value))}function bM(a,b){a=a||$,b=b||K,N.white&&!a.comment&&a.line===b.line&&bK(a,b)}function bL(a,b){a=a||$,b=b||K,N.white&&(a.character!==b.from||a.line!==b.line)&&bA(\"Unexpected space before '{a}'.\",b,b.value)}function bK(a,b){a=a||$,b=b||K,(N.white||bd===\"styleproperty\"||bd===\"style\")&&a.character!==b.from&&a.line===b.line&&bA(\"Unexpected space after '{a}'.\",b,a.value)}function bJ(a,b){var c;K.id===\"(end)\"&&bC(\"Unexpected early end of program.\",$),bI(),N.safe&&typeof O[$.value]===\"boolean\"&&K.id!==\"(\"&&K.id!==\".\"&&bA(\"ADsafe violation.\",$),b&&(e=\"anonymous\",v[\"(verb)\"]=$.value);if(b===!0&&$.fud)c=$.fud();else{if($.nud)c=$.nud();else{if(K.type===\"(number)\"&&$.id===\".\"){bA(\"A leading decimal point can be confused with a dot: '.{a}'.\",$,K.value),bI();return $}bC(\"Expected an identifier and instead saw '{a}'.\",$,$.id)}while(a<K.lbp)bI(),$.led?c=$.led(c):bC(\"Expected an operator and instead saw '{a}'.\",$,$.id)}return c}function bI(a,b){switch($.id){case\"(number)\":K.id===\".\"&&bA(\"A dot following a number can be confused with a decimal point.\",$);break;case\"-\":(K.id===\"-\"||K.id===\"--\")&&bA(\"Confusing minusses.\");break;case\"+\":(K.id===\"+\"||K.id===\"++\")&&bA(\"Confusing plusses.\")}if($.type===\"(string)\"||$.identifier)e=$.value;a&&K.id!==a&&(b?K.id===\"(end)\"?bA(\"Unmatched '{a}'.\",b,b.id):bA(\"Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'.\",K,a,b.id,b.line,K.value):(K.type!==\"(identifier)\"||K.value!==a)&&bA(\"Expected '{a}' and instead saw '{b}'.\",K,a,K.value)),Q=$,$=K;for(;;){K=H.shift()||bE.token();if(K.id===\"(end)\"||K.id===\"(error)\")return;if(K.type===\"special\")bG();else if(K.id!==\"(endline)\")break}}function bH(a){var b=a||0,c=0,d;while(c<=b)d=H[c],d||(d=H[c]=bE.token()),c+=1;return d}function bG(){var a,b,c,d=K.value,e,f;switch(d){case\"*/\":bC(\"Unbegun comment.\");break;case\"/*members\":case\"/*member\":d=\"/*members\",J||(J={}),b=J;break;case\"/*jshint\":N.safe&&bA(\"ADsafe restriction.\"),b=N,c=i;break;case\"/*global\":N.safe&&bA(\"ADsafe restriction.\"),b=O;break;default:bC(\"What?\")}e=bE.token();loop:for(;;){for(;;){if(e.type===\"special\"&&e.value===\"*/\")break loop;if(e.id!==\"(endline)\"&&e.id!==\",\")break;e=bE.token()}e.type!==\"(string)\"&&e.type!==\"(identifier)\"&&d!==\"/*members\"&&bC(\"Bad option.\",e),f=bE.token(),f.id===\":\"?(f=bE.token(),b===J&&bC(\"Expected '{a}' and instead saw '{b}'.\",e,\"*/\",\":\"),e.value===\"indent\"&&d===\"/*jshint\"?(a=+f.value,(typeof a!==\"number\"||!isFinite(a)||a<=0||Math.floor(a)!==a)&&bC(\"Expected a small integer and instead saw '{a}'.\",f,f.value),b.white=!0,b.indent=a):e.value===\"maxerr\"&&d===\"/*jshint\"?(a=+f.value,(typeof a!==\"number\"||!isFinite(a)||a<=0||Math.floor(a)!==a)&&bC(\"Expected a small integer and instead saw '{a}'.\",f,f.value),b.maxerr=a):e.value===\"maxlen\"&&d===\"/*jshint\"?(a=+f.value,(typeof a!==\"number\"||!isFinite(a)||a<=0||Math.floor(a)!==a)&&bC(\"Expected a small integer and instead saw '{a}'.\",f,f.value),b.maxlen=a):f.value===\"true\"?b[e.value]=!0:f.value===\"false\"?b[e.value]=!1:bC(\"Bad option value.\",f),e=bE.token()):(d===\"/*jshint\"&&bC(\"Missing option value.\",e),b[e.value]=!1,e=f)}c&&by()}function bF(a,b){N.safe&&v[\"(global)\"]&&typeof O[a]!==\"boolean\"?bA(\"ADsafe global: \"+a+\".\",$):a===\"hasOwnProperty\"&&bA(\"'hasOwnProperty' is a really bad name.\"),bw(v,a)&&!v[\"(global)\"]&&bA(v[a]===!0?\"'{a}' was used before it was defined.\":\"'{a}' is already defined.\",K,a),v[a]=b,v[\"(global)\"]?(y[a]=v,bw(B,a)&&(bA(\"'{a}' was used before it was defined.\",K,a),delete B[a])):S[a]=v}function bD(a,b,c,d,e,f,g){return bC(a,{line:b,from:c},d,e,f,g)}function bC(a,b,c,d,e,f){var g=bA(a,b,c,d,e,f);bz(\"Stopping, unable to continue.\",g.line,g.character)}function bB(a,b,c,d,e,f,g){return bA(a,{line:b,from:c},d,e,f,g)}function bA(a,b,c,e,f,g){var h,i,j;b=b||K,b.id===\"(end)\"&&(b=$),i=b.line||0,h=b.from||0,j={id:\"(error)\",raw:a,evidence:G[i-1]||\"\",line:i,character:h,a:c,b:e,c:f,d:g},j.reason=a.supplant(j),d.errors.push(j),N.passfail&&bz(\"Stopping. \",i,h),ba+=1,ba>=N.maxerr&&bz(\"Too many errors.\",i,h);return j}function bz(a,b,c){throw{name:\"JSHintError\",line:b,character:c,message:a+\" (\"+Math.floor(b/G.length*100)+\"% scanned).\"}}function by(){N.safe||(N.couch&&bx(O,k),N.rhino&&bx(O,R),N.node&&bx(O,L),N.devel&&bx(O,t),N.browser&&bx(O,j),N.jquery&&bx(O,F),N.windows&&bx(O,bc),N.widget&&bx(O,bb))}function bx(a,b){var c;for(c in b)bw(b,c)&&(a[c]=b[c])}function bw(a,b){return Object.prototype.hasOwnProperty.call(a,b)}function bv(){}\"use strict\";var a,b,c,e,f,g={\"<\":!0,\"<=\":!0,\"==\":!0,\"===\":!0,\"!==\":!0,\"!=\":!0,\">\":!0,\">=\":!0,\"+\":!0,\"-\":!0,\"*\":!0,\"/\":!0,\"%\":!0},h={arguments:!0,callee:!0,caller:!0,constructor:!0,eval:!0,prototype:!0,stack:!0,unwatch:!0,valueOf:!0,watch:!0},i={adsafe:!0,bitwise:!0,boss:!0,browser:!0,cap:!0,couch:!0,css:!0,curly:!0,debug:!0,devel:!0,eqeqeq:!0,es5:!0,evil:!0,forin:!0,fragment:!0,immed:!0,jquery:!0,laxbreak:!0,newcap:!0,noarg:!0,node:!0,noempty:!0,nonew:!0,nomen:!0,on:!0,onevar:!0,passfail:!0,plusplus:!0,regexp:!0,rhino:!0,undef:!0,safe:!0,windows:!0,strict:!0,sub:!0,white:!0,widget:!0},j={addEventListener:!1,applicationCache:!1,blur:!1,clearInterval:!1,clearTimeout:!1,close:!1,closed:!1,defaultStatus:!1,document:!1,event:!1,FileReader:!1,focus:!1,frames:!1,getComputedStyle:!1,history:!1,Image:!1,length:!1,localStorage:!1,location:!1,moveBy:!1,moveTo:!1,name:!1,navigator:!1,onbeforeunload:!0,onblur:!0,onerror:!0,onfocus:!0,onload:!0,onresize:!0,onunload:!0,open:!1,openDatabase:!1,opener:!1,Option:!1,parent:!1,print:!1,removeEventListener:!1,resizeBy:!1,resizeTo:!1,screen:!1,scroll:!1,scrollBy:!1,scrollTo:!1,setInterval:!1,setTimeout:!1,status:!1,top:!1,WebSocket:!1,window:!1,Worker:!1,XMLHttpRequest:!1},k={require:!1,respond:!1,getRow:!1,emit:!1,send:!1,start:!1,sum:!1,log:!1,exports:!1,module:!1},l,m,n={aliceblue:!0,antiquewhite:!0,aqua:!0,aquamarine:!0,azure:!0,beige:!0,bisque:!0,black:!0,blanchedalmond:!0,blue:!0,blueviolet:!0,brown:!0,burlywood:!0,cadetblue:!0,chartreuse:!0,chocolate:!0,coral:!0,cornflowerblue:!0,cornsilk:!0,crimson:!0,cyan:!0,darkblue:!0,darkcyan:!0,darkgoldenrod:!0,darkgray:!0,darkgreen:!0,darkkhaki:!0,darkmagenta:!0,darkolivegreen:!0,darkorange:!0,darkorchid:!0,darkred:!0,darksalmon:!0,darkseagreen:!0,darkslateblue:!0,darkslategray:!0,darkturquoise:!0,darkviolet:!0,deeppink:!0,deepskyblue:!0,dimgray:!0,dodgerblue:!0,firebrick:!0,floralwhite:!0,forestgreen:!0,fuchsia:!0,gainsboro:!0,ghostwhite:!0,gold:!0,goldenrod:!0,gray:!0,green:!0,greenyellow:!0,honeydew:!0,hotpink:!0,indianred:!0,indigo:!0,ivory:!0,khaki:!0,lavender:!0,lavenderblush:!0,lawngreen:!0,lemonchiffon:!0,lightblue:!0,lightcoral:!0,lightcyan:!0,lightgoldenrodyellow:!0,lightgreen:!0,lightpink:!0,lightsalmon:!0,lightseagreen:!0,lightskyblue:!0,lightslategray:!0,lightsteelblue:!0,lightyellow:!0,lime:!0,limegreen:!0,linen:!0,magenta:!0,maroon:!0,mediumaquamarine:!0,mediumblue:!0,mediumorchid:!0,mediumpurple:!0,mediumseagreen:!0,mediumslateblue:!0,mediumspringgreen:!0,mediumturquoise:!0,mediumvioletred:!0,midnightblue:!0,mintcream:!0,mistyrose:!0,moccasin:!0,navajowhite:!0,navy:!0,oldlace:!0,olive:!0,olivedrab:!0,orange:!0,orangered:!0,orchid:!0,palegoldenrod:!0,palegreen:!0,paleturquoise:!0,palevioletred:!0,papayawhip:!0,peachpuff:!0,peru:!0,pink:!0,plum:!0,powderblue:!0,purple:!0,red:!0,rosybrown:!0,royalblue:!0,saddlebrown:!0,salmon:!0,sandybrown:!0,seagreen:!0,seashell:!0,sienna:!0,silver:!0,skyblue:!0,slateblue:!0,slategray:!0,snow:!0,springgreen:!0,steelblue:!0,tan:!0,teal:!0,thistle:!0,tomato:!0,turquoise:!0,violet:!0,wheat:!0,white:!0,whitesmoke:!0,yellow:!0,yellowgreen:!0,activeborder:!0,activecaption:!0,appworkspace:!0,background:!0,buttonface:!0,buttonhighlight:!0,buttonshadow:!0,buttontext:!0,captiontext:!0,graytext:!0,highlight:!0,highlighttext:!0,inactiveborder:!0,inactivecaption:!0,inactivecaptiontext:!0,infobackground:!0,infotext:!0,menu:!0,menutext:!0,scrollbar:!0,threeddarkshadow:!0,threedface:!0,threedhighlight:!0,threedlightshadow:!0,threedshadow:!0,window:!0,windowframe:!0,windowtext:!0},o,p,q={\"%\":!0,cm:!0,em:!0,ex:!0,\"in\":!0,mm:!0,pc:!0,pt:!0,px:!0},r,s,t={alert:!1,confirm:!1,console:!1,Debug:!1,opera:!1,prompt:!1},u={\"\\b\":\"\\\\b\",\"\\t\":\"\\\\t\",\"\\n\":\"\\\\n\",\"\\f\":\"\\\\f\",\"\\r\":\"\\\\r\",'\"':'\\\\\"',\"/\":\"\\\\/\",\"\\\\\":\"\\\\\\\\\"},v,w=[\"closure\",\"exception\",\"global\",\"label\",\"outer\",\"unused\",\"var\"],x,y,z={a:{},abbr:{},acronym:{},address:{},applet:{},area:{empty:!0,parent:\" map \"},article:{},aside:{},audio:{},b:{},base:{empty:!0,parent:\" head \"},bdo:{},big:{},blockquote:{},body:{parent:\" html noframes \"},br:{empty:!0},button:{},canvas:{parent:\" body p div th td \"},caption:{parent:\" table \"},center:{},cite:{},code:{},col:{empty:!0,parent:\" table colgroup \"},colgroup:{parent:\" table \"},command:{parent:\" menu \"},datalist:{},dd:{parent:\" dl \"},del:{},details:{},dialog:{},dfn:{},dir:{},div:{},dl:{},dt:{parent:\" dl \"},em:{},embed:{},fieldset:{},figure:{},font:{},footer:{},form:{},frame:{empty:!0,parent:\" frameset \"},frameset:{parent:\" html frameset \"},h1:{},h2:{},h3:{},h4:{},h5:{},h6:{},head:{parent:\" html \"},header:{},hgroup:{},hr:{empty:!0},\"hta:application\":{empty:!0,parent:\" head \"},html:{parent:\"*\"},i:{},iframe:{},img:{empty:!0},input:{empty:!0},ins:{},kbd:{},keygen:{},label:{},legend:{parent:\" details fieldset figure \"},li:{parent:\" dir menu ol ul \"},link:{empty:!0,parent:\" head \"},map:{},mark:{},menu:{},meta:{empty:!0,parent:\" head noframes noscript \"},meter:{},nav:{},noframes:{parent:\" html body \"},noscript:{parent:\" body head noframes \"},object:{},ol:{},optgroup:{parent:\" select \"},option:{parent:\" optgroup select \"},output:{},p:{},param:{empty:!0,parent:\" applet object \"},pre:{},progress:{},q:{},rp:{},rt:{},ruby:{},samp:{},script:{empty:!0,parent:\" body div frame head iframe p pre span \"},section:{},select:{},small:{},span:{},source:{},strong:{},style:{parent:\" head \",empty:!0},sub:{},sup:{},table:{},tbody:{parent:\" table \"},td:{parent:\" tr \"},textarea:{},tfoot:{parent:\" table \"},th:{parent:\" tr \"},thead:{parent:\" table \"},time:{},title:{parent:\" head \"},tr:{parent:\" table tbody thead tfoot \"},tt:{},u:{},ul:{},\"var\":{},video:{}},A,B,C,D,E,F={$:!1,jQuery:!1},G,H,I,J,K,L={__filename:!1,__dirname:!1,Buffer:!1,GLOBAL:!1,global:!1,module:!1,process:!1,require:!1},M,N,O,P,Q,R={defineClass:!1,deserialize:!1,gc:!1,help:!1,load:!1,loadClass:!1,print:!1,quit:!1,readFile:!1,readUrl:!1,runCommand:!1,seal:!1,serialize:!1,spawn:!1,sync:!1,toint32:!1,version:!1},S,T,U,V={Array:!1,Boolean:!1,Date:!1,decodeURI:!1,decodeURIComponent:!1,encodeURI:!1,encodeURIComponent:!1,Error:!1,eval:!1,EvalError:!1,Function:!1,hasOwnProperty:!1,isFinite:!1,isNaN:!1,JSON:!1,Math:!1,Number:!1,Object:!1,parseInt:!1,parseFloat:!1,RangeError:!1,ReferenceError:!1,RegExp:!1,String:!1,SyntaxError:!1,TypeError:!1,URIError:!1},W={E:!0,LN2:!0,LN10:!0,LOG2E:!0,LOG10E:!0,MAX_VALUE:!0,MIN_VALUE:!0,NEGATIVE_INFINITY:!0,PI:!0,POSITIVE_INFINITY:!0,SQRT1_2:!0,SQRT2:!0},X,Y={},Z,$,_,ba,bb={alert:!0,animator:!0,appleScript:!0,beep:!0,bytesToUIString:!0,Canvas:!0,chooseColor:!0,chooseFile:!0,chooseFolder:!0,closeWidget:!0,COM:!0,convertPathToHFS:!0,convertPathToPlatform:!0,CustomAnimation:!0,escape:!0,FadeAnimation:!0,filesystem:!0,Flash:!0,focusWidget:!0,form:!0,FormField:!0,Frame:!0,HotKey:!0,Image:!0,include:!0,isApplicationRunning:!0,iTunes:!0,konfabulatorVersion:!0,log:!0,md5:!0,MenuItem:!0,MoveAnimation:!0,openURL:!0,play:!0,Point:!0,popupMenu:!0,preferenceGroups:!0,preferences:!0,print:!0,prompt:!0,random:!0,Rectangle:!0,reloadWidget:!0,ResizeAnimation:!0,resolvePath:!0,resumeUpdates:!0,RotateAnimation:!0,runCommand:!0,runCommandInBg:!0,saveAs:!0,savePreferences:!0,screen:!0,ScrollBar:!0,showWidgetPreferences:!0,sleep:!0,speak:!0,Style:!0,suppressUpdates:!0,system:!0,tellWidget:!0,Text:!0,TextArea:!0,Timer:!0,unescape:!0,updateNow:!0,URL:!0,Web:!0,widget:!0,Window:!0,XMLDOM:!0,XMLHttpRequest:!0,yahooCheckLogin:!0,yahooLogin:!0,yahooLogout:!0},bc={ActiveXObject:!1,CScript:!1,Debug:!1,Enumerator:!1,System:!1,VBArray:!1,WScript:!1},bd,be,bf=/@cc|<\\/?|script|\\]\\s*\\]|<\\s*!|&lt/i,bg=/[\\u0000-\\u001f\\u007f-\\u009f\\u00ad\\u0600-\\u0604\\u070f\\u17b4\\u17b5\\u200c-\\u200f\\u2028-\\u202f\\u2060-\\u206f\\ufeff\\ufff0-\\uffff]/,bh=/^\\s*([(){}\\[.,:;'\"~\\?\\]#@]|==?=?|\\/(\\*(jshint|members?|global)?|=|\\/)?|\\*[\\/=]?|\\+(?:=|\\++)?|-(?:=|-+)?|%=?|&[&=]?|\\|[|=]?|>>?>?=?|<([\\/=!]|\\!(\\[|--)?|<=?)?|\\^=?|\\!=?=?|[a-zA-Z_$][a-zA-Z0-9_$]*|[0-9]+([xX][0-9a-fA-F]+|\\.[0-9]*)?([eE][+\\-]?[0-9]+)?)/,bi=/^\\s*(['\"=>\\/&#]|<(?:\\/|\\!(?:--)?)?|[a-zA-Z][a-zA-Z0-9_\\-:]*|[0-9]+|--)/,bj=/[\\u0000-\\u001f&<\"\\/\\\\\\u007f-\\u009f\\u00ad\\u0600-\\u0604\\u070f\\u17b4\\u17b5\\u200c-\\u200f\\u2028-\\u202f\\u2060-\\u206f\\ufeff\\ufff0-\\uffff]/,bk=/[\\u0000-\\u001f&<\"\\/\\\\\\u007f-\\u009f\\u00ad\\u0600-\\u0604\\u070f\\u17b4\\u17b5\\u200c-\\u200f\\u2028-\\u202f\\u2060-\\u206f\\ufeff\\ufff0-\\uffff]/g,bl=/[>&]|<[\\/!]?|--/,bm=/\\*\\/|\\/\\*/,bn=/^([a-zA-Z_$][a-zA-Z0-9_$]*)$/,bo=/^(?:javascript|jscript|ecmascript|vbscript|mocha|livescript)\\s*:/i,bp=/&|\\+|\\u00AD|\\.\\.|\\/\\*|%[^;]|base64|url|expression|data|mailto/i,bq=/^\\s*([{:#%.=,>+\\[\\]@()\"';]|\\*=?|\\$=|\\|=|\\^=|~=|[a-zA-Z_][a-zA-Z0-9_\\-]*|[0-9]+|<\\/|\\/\\*)/,br=/^\\s*([@#!\"'};:\\-%.=,+\\[\\]()*_]|[a-zA-Z][a-zA-Z0-9._\\-]*|\\/\\*?|\\d+(?:\\.\\d+)?|<\\/)/,bs=/[^a-zA-Z0-9+\\-_\\/ ]/,bt=/[\\[\\]\\/\\\\\"'*<>.&:(){}+=#]/,bu={outer:bi,html:bi,style:bq,styleproperty:br};typeof Array.isArray!==\"function\"&&(Array.isArray=function(a){return Object.prototype.toString.apply(a)===\"[object Array]\"}),typeof Object.create!==\"function\"&&(Object.create=function(a){bv.prototype=a;return new bv}),typeof Object.keys!==\"function\"&&(Object.keys=function(a){var b=[],c;for(c in a)bw(a,c)&&b.push(c);return b}),typeof String.prototype.entityify!==\"function\"&&(String.prototype.entityify=function(){return this.replace(/&/g,\"&amp;\").replace(/</g,\"&lt;\").replace(/>/g,\"&gt;\")}),typeof String.prototype.isAlpha!==\"function\"&&(String.prototype.isAlpha=function(){return this>=\"a\"&&this<=\"z￿\"||this>=\"A\"&&this<=\"Z￿\"}),typeof String.prototype.isDigit!==\"function\"&&(String.prototype.isDigit=function(){return this>=\"0\"&&this<=\"9\"}),typeof String.prototype.supplant!==\"function\"&&(String.prototype.supplant=function(a){return this.replace(/\\{([^{}]*)\\}/g,function(b,c){var d=a[c];return typeof d===\"string\"||typeof d===\"number\"?d:b})}),typeof String.prototype.name!==\"function\"&&(String.prototype.name=function(){if(bn.test(this))return this;if(bj.test(this))return'\"'+this.replace(bk,function(a){var b=u[a];if(b)return b;return\"\\\\u\"+(\"0000\"+a.charCodeAt().toString(16)).slice(-4)})+'\"';return'\"'+this+'\"'});var bE=function bE(){function f(d,e){var f,g;d===\"(color)\"||d===\"(range)\"?g={type:d}:d===\"(punctuator)\"||d===\"(identifier)\"&&bw(Y,e)?g=Y[e]||Y[\"(error)\"]:g=Y[d],g=Object.create(g),(d===\"(string)\"||d===\"(range)\")&&bo.test(e)&&bB(\"Script URL.\",c,b),d===\"(identifier)\"&&(g.identifier=!0,e===\"__iterator__\"||e===\"__proto__\"?bD(\"Reserved name '{a}'.\",c,b,e):N.nomen&&(e.charAt(0)===\"_\"||e.charAt(e.length-1)===\"_\")&&bB(\"Unexpected {a} in '{b}'.\",c,b,\"dangling '_'\",e)),g.value=e,g.line=c,g.character=a,g.from=b,f=g.id,f!==\"(endline)\"&&(P=f&&(\"(,=:[!&|?{};\".indexOf(f.charAt(f.length-1))>=0||f===\"return\"));return g}function e(){var b;if(c>=G.length)return!1;a=1,d=G[c],c+=1,b=d.search(/ \\t/),b>=0&&bB(\"Mixed spaces and tabs.\",c,b+1),d=d.replace(/\\t/g,Z),b=d.search(bg),b>=0&&bB(\"Unsafe character.\",c,b),N.maxlen&&N.maxlen<d.length&&bB(\"Line too long.\",c,d.length);return!0}var a,b,c,d;return{init:function(a){typeof a===\"string\"?G=a.replace(/\\r\\n/g,\"\\n\").replace(/\\r/g,\"\\n\").split(\"\\n\"):G=a,c=0,e(),b=1},range:function(e,g){var h,i=\"\";b=a,d.charAt(0)!==e&&bD(\"Expected '{a}' and instead saw '{b}'.\",c,a,e,d.charAt(0));for(;;){d=d.slice(1),a+=1,h=d.charAt(0);switch(h){case\"\":bD(\"Missing '{a}'.\",c,a,h);break;case g:d=d.slice(1),a+=1;return f(\"(range)\",i);case be:case\"\\\\\":bB(\"Unexpected '{a}'.\",c,a,h)}i+=h}},token:function(){function s(g){function k(b){var e=parseInt(d.substr(i+1,b),16);i+=b,e>=32&&e<=126&&e!==34&&e!==92&&e!==39&&bB(\"Unnecessary escapement.\",c,a),a+=b,h=String.fromCharCode(e)}var h,i,j=\"\";E&&g!=='\"'&&bB(\"Strings must use doublequote.\",c,a);if(be===g||bd===\"scriptstring\"&&!be)return f(\"(punctuator)\",g);i=0;for(;;){while(i>=d.length)i=0,(bd!==\"html\"||!e())&&bD(\"Unclosed string.\",c,b);h=d.charAt(i);if(h===g){a+=1,d=d.substr(i+1);return f(\"(string)\",j,g)}if(h<\" \"){if(h===\"\\n\"||h===\"\\r\")break;bB(\"Control character in string: {a}.\",c,a+i,d.slice(0,i))}else if(h===be)bB(\"Bad HTML string\",c,a+i);else if(h===\"<\")N.safe&&bd===\"html\"?bB(\"ADsafe string violation.\",c,a+i):d.charAt(i+1)===\"/\"&&(bd||N.safe)?bB(\"Expected '<\\\\/' and instead saw '</'.\",c,a):d.charAt(i+1)===\"!\"&&(bd||N.safe)&&bB(\"Unexpected '<!' in a string.\",c,a);else if(h===\"\\\\\")if(bd===\"html\")N.safe&&bB(\"ADsafe string violation.\",c,a+i);else if(bd===\"styleproperty\")i+=1,a+=1,h=d.charAt(i),h!==g&&bB(\"Escapement in style string.\",c,a+i);else{i+=1,a+=1,h=d.charAt(i);switch(h){case be:bB(\"Bad HTML string\",c,a+i);break;case\"\\\\\":case'\"':case\"/\":break;case\"'\":E&&bB(\"Avoid \\\\'.\",c,a);break;case\"b\":h=\"\\b\";break;case\"f\":h=\"\\f\";break;case\"n\":h=\"\\n\";break;case\"r\":h=\"\\r\";break;case\"t\":h=\"\\t\";break;case\"u\":k(4);break;case\"v\":E&&bB(\"Avoid \\\\v.\",c,a),h=\"\u000b\";break;case\"x\":E&&bB(\"Avoid \\\\x-.\",c,a),k(2);break;default:bB(\"Bad escapement.\",c,a)}}j+=h,a+=1,i+=1}}function r(c){var e=c.exec(d),f;if(e){n=e[0].length,f=e[1],h=f.charAt(0),d=d.substr(n),b=a+n-f.length,a+=n;return f}}var g,h,i,j,k,l,m,n,o,p,q;for(;;){if(!d)return f(e()?\"(endline)\":\"(end)\",\"\");while(bd===\"outer\"){m=d.search(bl);if(m===0)break;if(m>0){a+=1,d=d.slice(m);break}if(!e())return f(\"(end)\",\"\")}q=r(bu[bd]||bh);if(!q){q=\"\",h=\"\";while(d&&d<\"!\")d=d.substr(1);if(d){if(bd===\"html\")return f(\"(error)\",d.charAt(0));bD(\"Unexpected '{a}'.\",c,a,d.substr(0,1))}}else{if(h.isAlpha()||h===\"_\"||h===\"$\")return f(\"(identifier)\",q);if(h.isDigit()){bd!==\"style\"&&!isFinite(Number(q))&&bB(\"Bad number '{a}'.\",c,a,q),bd!==\"style\"&&bd!==\"styleproperty\"&&d.substr(0,1).isAlpha()&&bB(\"Missing space after '{a}'.\",c,a,q),h===\"0\"&&(j=q.substr(1,1),j.isDigit()?$.id!==\".\"&&bd!==\"styleproperty\"&&bB(\"Don't use extra leading zeros '{a}'.\",c,a,q):E&&(j===\"x\"||j===\"X\")&&bB(\"Avoid 0x-. '{a}'.\",c,a,q)),q.substr(q.length-1)===\".\"&&bB(\"A trailing decimal point can be confused with a dot '{a}'.\",c,a,q);return f(\"(number)\",q)}switch(q){case'\"':case\"'\":return s(q);case\"//\":T||bd&&bd!==\"script\"?bB(\"Unexpected comment.\",c,a):bd===\"script\"&&/<\\s*\\//i.test(d)?bB(\"Unexpected </ in comment.\",c,a):(N.safe||bd===\"script\")&&bf.test(d)&&bB(\"Dangerous comment.\",c,a),d=\"\",$.comment=!0;break;case\"/*\":(T||bd&&bd!==\"script\"&&bd!==\"style\"&&bd!==\"styleproperty\")&&bB(\"Unexpected comment.\",c,a),N.safe&&bf.test(d)&&bB(\"ADsafe comment violation.\",c,a);for(;;){m=d.search(bm);if(m>=0)break;e()?N.safe&&bf.test(d)&&bB(\"ADsafe comment violation.\",c,a):bD(\"Unclosed comment.\",c,a)}a+=m+2,d.substr(m,1)===\"/\"&&bD(\"Nested comment.\",c,a),d=d.substr(m+2),$.comment=!0;break;case\"/*members\":case\"/*member\":case\"/*jshint\":case\"/*global\":case\"*/\":return{value:q,type:\"special\",line:c,character:a,from:b};case\"\":break;case\"/\":$.id===\"/=\"&&bD(\"A regular expression literal can be confused with '/='.\",c,b);if(P){k=0,i=0,n=0;for(;;){g=!0,h=d.charAt(n),n+=1;switch(h){case\"\":bD(\"Unclosed regular expression.\",c,b);return;case\"/\":k>0&&bB(\"Unescaped '{a}'.\",c,b+n,\"/\"),h=d.substr(0,n-1),p={g:!0,i:!0,m:!0};while(p[d.charAt(n)]===!0)p[d.charAt(n)]=!1,n+=1;a+=n,d=d.substr(n),p=d.charAt(0),(p===\"/\"||p===\"*\")&&bD(\"Confusing regular expression.\",c,b);return f(\"(regexp)\",h);case\"\\\\\":h=d.charAt(n),h<\" \"?bB(\"Unexpected control character in regular expression.\",c,b+n):h===\"<\"&&bB(\"Unexpected escaped character '{a}' in regular expression.\",c,b+n,h),n+=1;break;case\"(\":k+=1,g=!1;if(d.charAt(n)===\"?\"){n+=1;switch(d.charAt(n)){case\":\":case\"=\":case\"!\":n+=1;break;default:bB(\"Expected '{a}' and instead saw '{b}'.\",c,b+n,\":\",d.charAt(n))}}else i+=1;break;case\"|\":g=!1;break;case\")\":k===0?bB(\"Unescaped '{a}'.\",c,b+n,\")\"):k-=1;break;case\" \":p=1;while(d.charAt(n)===\" \")n+=1,p+=1;p>1&&bB(\"Spaces are hard to count. Use {{a}}.\",c,b+n,p);break;case\"[\":h=d.charAt(n),h===\"^\"&&(n+=1,N.regexp?bB(\"Insecure '{a}'.\",c,b+n,h):d.charAt(n)===\"]\"&&bD(\"Unescaped '{a}'.\",c,b+n,\"^\")),p=!1,h===\"]\"&&(bB(\"Empty class.\",c,b+n-1),p=!0);klass:do{h=d.charAt(n),n+=1;switch(h){case\"[\":case\"^\":bB(\"Unescaped '{a}'.\",c,b+n,h),p=!0;break;case\"-\":p?p=!1:(bB(\"Unescaped '{a}'.\",c,b+n,\"-\"),p=!0);break;case\"]\":p||bB(\"Unescaped '{a}'.\",c,b+n-1,\"-\");break klass;case\"\\\\\":h=d.charAt(n),h<\" \"?bB(\"Unexpected control character in regular expression.\",c,b+n):h===\"<\"&&bB(\"Unexpected escaped character '{a}' in regular expression.\",c,b+n,h),n+=1,p=!0;break;case\"/\":bB(\"Unescaped '{a}'.\",c,b+n-1,\"/\"),p=!0;break;case\"<\":bd===\"script\"&&(h=d.charAt(n),(h===\"!\"||h===\"/\")&&bB(\"HTML confusion in regular expression '<{a}'.\",c,b+n,h)),p=!0;break;default:p=!0}}while(h);break;case\".\":N.regexp&&bB(\"Insecure '{a}'.\",c,b+n,h);break;case\"]\":case\"?\":case\"{\":case\"}\":case\"+\":case\"*\":bB(\"Unescaped '{a}'.\",c,b+n,h);break;case\"<\":bd===\"script\"&&(h=d.charAt(n),(h===\"!\"||h===\"/\")&&bB(\"HTML confusion in regular expression '<{a}'.\",c,b+n,h))}if(g)switch(d.charAt(n)){case\"?\":case\"+\":case\"*\":n+=1,d.charAt(n)===\"?\"&&(n+=1);break;case\"{\":n+=1,h=d.charAt(n),(h<\"0\"||h>\"9\")&&bB(\"Expected a number and instead saw '{a}'.\",c,b+n,h),n+=1,o=+h;for(;;){h=d.charAt(n);if(h<\"0\"||h>\"9\")break;n+=1,o=+h+o*10}l=o;if(h===\",\"){n+=1,l=Infinity,h=d.charAt(n);if(h>=\"0\"&&h<=\"9\"){n+=1,l=+h;for(;;){h=d.charAt(n);if(h<\"0\"||h>\"9\")break;n+=1,l=+h+l*10}}}d.charAt(n)!==\"}\"?bB(\"Expected '{a}' and instead saw '{b}'.\",c,b+n,\"}\",h):n+=1,d.charAt(n)===\"?\"&&(n+=1),o>l&&bB(\"'{a}' should not be greater than '{b}'.\",c,b+n,o,l)}}h=d.substr(0,n-1),a+=n,d=d.substr(n);return f(\"(regexp)\",h)}return f(\"(punctuator)\",q);case\"<!--\":n=c,h=a;for(;;){m=d.indexOf(\"--\");if(m>=0)break;m=d.indexOf(\"<!\"),m>=0&&bD(\"Nested HTML comment.\",c,a+m),e()||bD(\"Unclosed HTML comment.\",n,h)}n=d.indexOf(\"<!\"),n>=0&&n<m&&bD(\"Nested HTML comment.\",c,a+n),a+=m,d.charAt(m+2)!==\">\"&&bD(\"Expected -->.\",c,a),a+=3,d=d.slice(m+3);break;case\"#\":if(bd===\"html\"||bd===\"styleproperty\"){for(;;){h=d.charAt(0);if((h<\"0\"||h>\"9\")&&(h<\"a\"||h>\"f\")&&(h<\"A\"||h>\"F\"))break;a+=1,d=d.substr(1),q+=h}q.length!==4&&q.length!==7&&bB(\"Bad hex color '{a}'.\",c,b+n,q);return f(\"(color)\",q)}return f(\"(punctuator)\",q);default:if(bd===\"outer\"&&h===\"&\"){a+=1,d=d.substr(1);for(;;){h=d.charAt(0),a+=1,d=d.substr(1);if(h===\";\")break;h>=\"0\"&&h<=\"9\"||h>=\"a\"&&h<=\"z\"||h===\"#\"||bD(\"Bad entity\",c,b+n,a)}break}return f(\"(punctuator)\",q)}}}}}}();m=[cB,function(){for(;;)if(K.identifier)switch(K.value.toLowerCase()){case\"url\":cB();break;case\"expression\":bA(\"Unexpected expression '{a}'.\",K,K.value),bI();break;default:bI()}else{if(K.id===\";\"||K.id===\"!\"||K.id===\"(end)\"||K.id===\"}\")return!0;bI()}}],o=[\"none\",\"dashed\",\"dotted\",\"double\",\"groove\",\"hidden\",\"inset\",\"outset\",\"ridge\",\"solid\"],p=[\"auto\",\"always\",\"avoid\",\"left\",\"right\"],r={all:!0,braille:!0,embossed:!0,handheld:!0,print:!0,projection:!0,screen:!0,speech:!0,tty:!0,tv:!0},s=[\"auto\",\"hidden\",\"scroll\",\"visible\"],l={background:[!0,\"background-attachment\",\"background-color\",\"background-image\",\"background-position\",\"background-repeat\"],\"background-attachment\":[\"scroll\",\"fixed\"],\"background-color\":[\"transparent\",cs],\"background-image\":[\"none\",cB],\"background-position\":[2,[ct,\"top\",\"bottom\",\"left\",\"right\",\"center\"]],\"background-repeat\":[\"repeat\",\"repeat-x\",\"repeat-y\",\"no-repeat\"],border:[!0,\"border-color\",\"border-style\",\"border-width\"],\"border-bottom\":[!0,\"border-bottom-color\",\"border-bottom-style\",\"border-bottom-width\"],\"border-bottom-color\":cs,\"border-bottom-style\":o,\"border-bottom-width\":cv,\"border-collapse\":[\"collapse\",\"separate\"],\"border-color\":[\"transparent\",4,cs],\"border-left\":[!0,\"border-left-color\",\"border-left-style\",\"border-left-width\"],\"border-left-color\":cs,\"border-left-style\":o,\"border-left-width\":cv,\"border-right\":[!0,\"border-right-color\",\"border-right-style\",\"border-right-width\"],\"border-right-color\":cs,\"border-right-style\":o,\"border-right-width\":cv,\"border-spacing\":[2,ct],\"border-style\":[4,o],\"border-top\":[!0,\"border-top-color\",\"border-top-style\",\"border-top-width\"],\"border-top-color\":cs,\"border-top-style\":o,\"border-top-width\":cv,\"border-width\":[4,cv],bottom:[ct,\"auto\"],\"caption-side\":[\"bottom\",\"left\",\"right\",\"top\"],clear:[\"both\",\"left\",\"none\",\"right\"],clip:[cA,\"auto\"],color:cs,content:[\"open-quote\",\"close-quote\",\"no-open-quote\",\"no-close-quote\",cr,cB,cz,cx],\"counter-increment\":[cp,\"none\"],\"counter-reset\":[cp,\"none\"],cursor:[cB,\"auto\",\"crosshair\",\"default\",\"e-resize\",\"help\",\"move\",\"n-resize\",\"ne-resize\",\"nw-resize\",\"pointer\",\"s-resize\",\"se-resize\",\"sw-resize\",\"w-resize\",\"text\",\"wait\"],direction:[\"ltr\",\"rtl\"],display:[\"block\",\"compact\",\"inline\",\"inline-block\",\"inline-table\",\"list-item\",\"marker\",\"none\",\"run-in\",\"table\",\"table-caption\",\"table-cell\",\"table-column\",\"table-column-group\",\"table-footer-group\",\"table-header-group\",\"table-row\",\"table-row-group\"],\"empty-cells\":[\"show\",\"hide\"],\"float\":[\"left\",\"none\",\"right\"],font:[\"caption\",\"icon\",\"menu\",\"message-box\",\"small-caption\",\"status-bar\",!0,\"font-size\",\"font-style\",\"font-weight\",\"font-family\"],\"font-family\":cy,\"font-size\":[\"xx-small\",\"x-small\",\"small\",\"medium\",\"large\",\"x-large\",\"xx-large\",\"larger\",\"smaller\",ct],\"font-size-adjust\":[\"none\",cq],\"font-stretch\":[\"normal\",\"wider\",\"narrower\",\"ultra-condensed\",\"extra-condensed\",\"condensed\",\"semi-condensed\",\"semi-expanded\",\"expanded\",\"extra-expanded\"],\"font-style\":[\"normal\",\"italic\",\"oblique\"],\"font-variant\":[\"normal\",\"small-caps\"],\"font-weight\":[\"normal\",\"bold\",\"bolder\",\"lighter\",cq],height:[ct,\"auto\"],left:[ct,\"auto\"],\"letter-spacing\":[\"normal\",ct],\"line-height\":[\"normal\",cu],\"list-style\":[!0,\"list-style-image\",\"list-style-position\",\"list-style-type\"],\"list-style-image\":[\"none\",cB],\"list-style-position\":[\"inside\",\"outside\"],\"list-style-type\":[\"circle\",\"disc\",\"square\",\"decimal\",\"decimal-leading-zero\",\"lower-roman\",\"upper-roman\",\"lower-greek\",\"lower-alpha\",\"lower-latin\",\"upper-alpha\",\"upper-latin\",\"hebrew\",\"katakana\",\"hiragana-iroha\",\"katakana-oroha\",\"none\"],margin:[4,cw],\"margin-bottom\":cw,\"margin-left\":cw,\"margin-right\":cw,\"margin-top\":cw,\"marker-offset\":[ct,\"auto\"],\"max-height\":[ct,\"none\"],\"max-width\":[ct,\"none\"],\"min-height\":ct,\"min-width\":ct,opacity:cq,outline:[!0,\"outline-color\",\"outline-style\",\"outline-width\"],\"outline-color\":[\"invert\",cs],\"outline-style\":[\"dashed\",\"dotted\",\"double\",\"groove\",\"inset\",\"none\",\"outset\",\"ridge\",\"solid\"],\"outline-width\":cv,overflow:s,\"overflow-x\":s,\"overflow-y\":s,padding:[4,ct],\"padding-bottom\":ct,\"padding-left\":ct,\"padding-right\":ct,\"padding-top\":ct,\"page-break-after\":p,\"page-break-before\":p,position:[\"absolute\",\"fixed\",\"relative\",\"static\"],quotes:[8,cr],right:[ct,\"auto\"],\"table-layout\":[\"auto\",\"fixed\"],\"text-align\":[\"center\",\"justify\",\"left\",\"right\"],\"text-decoration\":[\"none\",\"underline\",\"overline\",\"line-through\",\"blink\"],\"text-indent\":ct,\"text-shadow\":[\"none\",4,[cs,ct]],\"text-transform\":[\"capitalize\",\"uppercase\",\"lowercase\",\"none\"],top:[ct,\"auto\"],\"unicode-bidi\":[\"normal\",\"embed\",\"bidi-override\"],\"vertical-align\":[\"baseline\",\"bottom\",\"sub\",\"super\",\"top\",\"text-top\",\"middle\",\"text-bottom\",ct],visibility:[\"visible\",\"hidden\",\"collapse\"],\"white-space\":[\"normal\",\"nowrap\",\"pre\",\"pre-line\",\"pre-wrap\",\"inherit\"],width:[ct,\"auto\"],\"word-spacing\":[\"normal\",ct],\"word-wrap\":[\"break-word\",\"normal\"],\"z-index\":[\"auto\",cq]},bY(\"(number)\",function(){return this}),bY(\"(string)\",function(){return this}),Y[\"(identifier)\"]={type:\"(identifier)\",lbp:0,identifier:!0,nud:function(){var a=this.value,b=S[a],c;typeof b===\"function\"?b=undefined:typeof b===\"boolean\"&&(c=v,v=x[0],bF(a,\"var\"),b=v,v=c);if(v===b)switch(v[a]){case\"unused\":v[a]=\"var\";break;case\"unction\":v[a]=\"function\",this[\"function\"]=!0;break;case\"function\":this[\"function\"]=!0;break;case\"label\":bA(\"'{a}' is a statement label.\",$,a)}else if(v[\"(global)\"])e!=\"typeof\"&&e!=\"delete\"&&N.undef&&typeof O[a]!==\"boolean\"&&bA(\"'{a}' is not defined.\",$,a),co($);else switch(v[a]){case\"closure\":case\"function\":case\"var\":case\"unused\":bA(\"'{a}' used out of scope.\",$,a);break;case\"label\":bA(\"'{a}' is a statement label.\",$,a);break;case\"outer\":case\"global\":break;default:if(b===!0)v[a]=!0;else if(b===null)bA(\"'{a}' is not allowed.\",$,a),co($);else if(typeof b!==\"object\")e!=\"typeof\"&&e!=\"delete\"&&N.undef?bA(\"'{a}' is not defined.\",$,a):v[a]=!0,co($);else switch(b[a]){case\"function\":case\"unction\":this[\"function\"]=!0,b[a]=\"closure\",v[a]=b[\"(global)\"]?\"global\":\"outer\";break;case\"var\":case\"unused\":b[a]=\"closure\",v[a]=b[\"(global)\"]?\"global\":\"outer\";break;case\"closure\":case\"parameter\":v[a]=b[\"(global)\"]?\"global\":\"outer\";break;case\"label\":bA(\"'{a}' is a statement label.\",$,a)}}return this},led:function(){bC(\"Expected an operator and instead saw '{a}'.\",K,K.value)}},bY(\"(regexp)\",function(){return this}),bT(\"(endline)\"),bT(\"(begin)\"),bT(\"(end)\").reach=!0,bT(\"</\").reach=!0,bT(\"<!\"),bT(\"<!--\"),bT(\"-->\"),bT(\"(error)\").reach=!0,bT(\"}\").reach=!0,bT(\")\"),bT(\"]\"),bT('\"').reach=!0,bT(\"'\").reach=!0,bT(\";\"),bT(\":\").reach=!0,bT(\",\"),bT(\"#\"),bT(\"@\"),bZ(\"else\"),bZ(\"case\").reach=!0,bZ(\"catch\"),bZ(\"default\").reach=!0,bZ(\"finally\"),b$(\"arguments\",function(a){X&&v[\"(global)\"]?bA(\"Strict violation.\",a):N.safe&&bA(\"ADsafe violation.\",a)}),b$(\"eval\",function(a){N.safe&&bA(\"ADsafe violation.\",a)}),b$(\"false\"),b$(\"Infinity\"),b$(\"NaN\"),b$(\"null\"),b$(\"this\",function(a){X&&(v[\"(statement)\"]&&v[\"(name)\"].charAt(0)>\"Z\"||v[\"(global)\"])?bA(\"Strict violation.\",a):N.safe&&bA(\"ADsafe violation.\",a)}),b$(\"true\"),b$(\"undefined\"),cc(\"=\",\"assign\",20),cc(\"+=\",\"assignadd\",20),cc(\"-=\",\"assignsub\",20),cc(\"*=\",\"assignmult\",20),cc(\"/=\",\"assigndiv\",20).nud=function(){bC(\"A regular expression literal can be confused with '/='.\")},cc(\"%=\",\"assignmod\",20),ce(\"&=\",\"assignbitand\",20),ce(\"|=\",\"assignbitor\",20),ce(\"^=\",\"assignbitxor\",20),ce(\"<<=\",\"assignshiftleft\",20),ce(\">>=\",\"assignshiftright\",20),ce(\">>>=\",\"assignshiftrightunsigned\",20),b_(\"?\",function(a,b){b.left=a,b.right=bJ(10),bI(\":\"),b[\"else\"]=bJ(10);return b},30),b_(\"||\",\"or\",40),b_(\"&&\",\"and\",50),cd(\"|\",\"bitor\",70),cd(\"^\",\"bitxor\",80),cd(\"&\",\"bitand\",90),ca(\"==\",function(a,b){N.eqeqeq?bA(\"Expected '{a}' and instead saw '{b}'.\",this,\"===\",\"==\"):cb(a)?bA(\"Use '{a}' to compare with '{b}'.\",this,\"===\",a.value):cb(b)&&bA(\"Use '{a}' to compare with '{b}'.\",this,\"===\",b.value);return this}),ca(\"===\"),ca(\"!=\",function(a,b){N.eqeqeq?bA(\"Expected '{a}' and instead saw '{b}'.\",this,\"!==\",\"!=\"):cb(a)?bA(\"Use '{a}' to compare with '{b}'.\",this,\"!==\",a.value):cb(b)&&bA(\"Use '{a}' to compare with '{b}'.\",this,\"!==\",b.value);return this}),ca(\"!==\"),ca(\"<\"),ca(\">\"),ca(\"<=\"),ca(\">=\"),cd(\"<<\",\"shiftleft\",120),cd(\">>\",\"shiftright\",120),cd(\">>>\",\"shiftrightunsigned\",120),b_(\"in\",\"in\",120),b_(\"instanceof\",\"instanceof\",120),b_(\"+\",function(a,b){var c=bJ(130);if(a&&c&&a.id===\"(string)\"&&c.id===\"(string)\"){a.value+=c.value,a.character=c.character,bo.test(a.value)&&bA(\"JavaScript URL.\",a);return a}b.left=a,b.right=c;return b},130),bX(\"+\",\"num\"),bX(\"+++\",function(){bA(\"Confusing pluses.\"),this.right=bJ(150),this.arity=\"unary\";return this}),b_(\"+++\",function(a){bA(\"Confusing pluses.\"),this.left=a,this.right=bJ(130);return this},130),b_(\"-\",\"sub\",130),bX(\"-\",\"neg\"),bX(\"---\",function(){bA(\"Confusing minuses.\"),this.right=bJ(150),this.arity=\"unary\";return this}),b_(\"---\",function(a){bA(\"Confusing minuses.\"),this.left=a,this.right=bJ(130);return this},130),b_(\"*\",\"mult\",140),b_(\"/\",\"div\",140),b_(\"%\",\"mod\",140),cf(\"++\",\"postinc\"),bX(\"++\",\"preinc\"),Y[\"++\"].exps=!0,cf(\"--\",\"postdec\"),bX(\"--\",\"predec\"),Y[\"--\"].exps=!0,bX(\"delete\",function(){var a=bJ(0);(!a||a.id!==\".\"&&a.id!==\"[\")&&bA(\"Variables should not be deleted.\"),this.first=a;return this}).exps=!0,bX(\"~\",function(){N.bitwise&&bA(\"Unexpected '{a}'.\",this,\"~\"),bJ(150);return this}),bX(\"!\",function(){this.right=bJ(150),this.arity=\"unary\",g[this.right.id]===!0&&bA(\"Confusing use of '{a}'.\",this,\"!\");return this}),bX(\"typeof\",\"typeof\"),bX(\"new\",function(){var a=bJ(155),b;if(a&&a.id!==\"function\")if(a.identifier){a[\"new\"]=!0;switch(a.value){case\"Object\":bA(\"Use the object literal notation {}.\",$);break;case\"Array\":K.id!==\"(\"?bA(\"Use the array literal notation [].\",$):(bI(\"(\"),K.id===\")\"&&bA(\"Use the array literal notation [].\",$),bI(\")\")),this.first=a;return this;case\"Number\":case\"String\":case\"Boolean\":case\"Math\":case\"JSON\":bA(\"Do not use {a} as a constructor.\",$,a.value);break;case\"Function\":N.evil||bA(\"The Function constructor is eval.\");break;case\"Date\":case\"RegExp\":break;default:a.id!==\"function\"&&(b=a.value.substr(0,1),N.newcap&&(b<\"A\"||b>\"Z\")&&bA(\"A constructor name should start with an uppercase letter.\",$))}}else a.id!==\".\"&&a.id!==\"[\"&&a.id!==\"(\"&&bA(\"Bad constructor.\",$);else bA(\"Weird construction. Delete 'new'.\",this);bK($,K),K.id!==\"(\"&&bA(\"Missing '()' invoking a constructor.\"),this.first=a;return this}),Y[\"new\"].exps=!0,b_(\".\",function(d,e){bK(Q,$),bL();var f=ch();typeof f===\"string\"&&cn(f),e.left=d,e.right=f,N.noarg&&d&&d.value===\"arguments\"&&(f===\"callee\"||f===\"caller\")?bA(\"Avoid arguments.{a}.\",d,f):!N.evil&&d&&d.value===\"document\"&&(f===\"write\"||f===\"writeln\")?bA(\"document.write can be a form of eval.\",d):N.adsafe&&d&&d.value===\"ADSAFE\"&&(f===\"id\"||f===\"lib\"?bA(\"ADsafe violation.\",e):f===\"go\"&&(bd!==\"script\"?bA(\"ADsafe violation.\",e):(c||K.id!==\"(\"||bH(0).id!==\"(string)\"||bH(0).value!==a||bH(1).id!==\",\")&&bC(\"ADsafe violation: go.\",e),c=!0,b=!1));if(!!N.evil||f!==\"eval\"&&f!==\"execScript\"){if(N.safe)for(;;){h[f]===!0&&bA(\"ADsafe restricted word '{a}'.\",$,f);if(typeof O[d.value]!==\"boolean\"||K.id===\"(\")break;if(W[f]===!0){K.id===\".\"&&bA(\"ADsafe violation.\",e);break}if(K.id!==\".\"){bA(\"ADsafe violation.\",e);break}bI(\".\"),$.left=e,$.right=f,e=$,f=ch(),typeof f===\"string\"&&cn(f)}}else bA(\"eval is evil.\");return e},160,!0),b_(\"(\",function(a,b){Q.id!==\"}\"&&Q.id!==\")\"&&bL(Q,$),bM(),N.immed&&!a.immed&&a.id===\"function\"&&bA(\"Wrap an immediate function invocation in parentheses to assist the reader in understanding that the expression is the result of a function, and not the function itself.\");var c=0,d=[];a&&(a.type===\"(identifier)\"?a.value.match(/^[A-Z]([A-Z0-9_$]*[a-z][A-Za-z0-9_$]*)?$/)&&a.value!==\"Number\"&&a.value!==\"String\"&&a.value!==\"Boolean\"&&a.value!==\"Date\"&&(a.value===\"Math\"?bA(\"Math is not a function.\",a):N.newcap&&bA(\"Missing 'new' prefix when invoking a constructor.\",a)):a.id===\".\"&&N.safe&&a.left.value===\"Math\"&&a.right===\"random\"&&bA(\"ADsafe violation.\",a));if(K.id!==\")\")for(;;){d[d.length]=bJ(10),c+=1;if(K.id!==\",\")break;bR()}bI(\")\"),bM(Q,$),typeof a===\"object\"&&(a.value===\"parseInt\"&&c===1&&bA(\"Missing radix parameter.\",a),N.evil||(a.value===\"eval\"||a.value===\"Function\"||a.value===\"execScript\"?bA(\"eval is evil.\",a):d[0]&&d[0].id===\"(string)\"&&(a.value===\"setTimeout\"||a.value===\"setInterval\")&&bA(\"Implied eval is evil. Pass a function instead of a string.\",a)),!a.identifier&&a.id!==\".\"&&a.id!==\"[\"&&a.id!==\"(\"&&a.id!==\"&&\"&&a.id!==\"||\"&&a.id!==\"?\"&&bA(\"Bad invocation.\",a)),b.left=a;return b},155,!0).exps=!0,bX(\"(\",function(){bM(),K.id===\"function\"&&(K.immed=!0);var a=bJ(0);bI(\")\",this),bM(Q,$),N.immed&&a.id===\"function\"&&(K.id===\"(\"?bA(\"Move the invocation into the parens that contain the function.\",K):bA(\"Do not wrap function literals in parens unless they are to be immediately invoked.\",this));return a}),b_(\"[\",function(a,b){bL(Q,$),bM();var c=bJ(0),d;c&&c.type===\"(string)\"?(N.safe&&h[c.value]===!0?bA(\"ADsafe restricted word '{a}'.\",b,c.value):!!N.evil||c.value!==\"eval\"&&c.value!==\"execScript\"?N.safe&&(c.value.charAt(0)===\"_\"||c.value.charAt(0)===\"-\")&&bA(\"ADsafe restricted subscript '{a}'.\",b,c.value):bA(\"eval is evil.\",b),cn(c.value),!N.sub&&bn.test(c.value)&&(d=Y[c.value],(!d||!d.reserved)&&bA(\"['{a}'] is better written in dot notation.\",c,c.value))):(!c||c.type!==\"(number)\"||c.value<0)&&N.safe&&bA(\"ADsafe subscripting.\"),bI(\"]\",b),bM(Q,$),b.left=a,b.right=c;return b},160,!0),bX(\"[\",function(){var a=$.line!==K.line;this.first=[],a&&(D+=N.indent,K.from===D+N.indent&&(D+=N.indent));while(K.id!==\"(end)\"){while(K.id===\",\")bA(\"Extra comma.\"),bI(\",\");if(K.id===\"]\")break;a&&$.line!==K.line&&bP(),this.first.push(bJ(10));if(K.id!==\",\")break;bR();if(K.id===\"]\"&&!N.es5){bA(\"Extra comma.\",$);break}}a&&(D-=N.indent,bP()),bI(\"]\",this);return this},160),function(a){a.nud=function(){var a,b,c,d,e,f={},g;a=$.line!==K.line,a&&(D+=N.indent,K.from===D+N.indent&&(D+=N.indent));for(;;){if(K.id===\"}\")break;a&&bP();if(K.value===\"get\"&&bH().id!==\":\")bI(\"get\"),N.es5||bC(\"get/set are ES5 features.\"),c=cP(),c||bC(\"Missing property name.\"),g=K,bK($,K),b=cR(c),v[\"(loopage)\"]&&bA(\"Don't make functions within a loop.\",g),e=b[\"(params)\"],e&&bA(\"Unexpected parameter '{a}' in get {b} function.\",g,e[0],c),bK($,K),bI(\",\"),bP(),bI(\"set\"),d=cP(),c!==d&&bC(\"Expected {a} and instead saw {b}.\",$,c,d),g=K,bK($,K),b=cR(c),e=b[\"(params)\"],(!e||e.length!==1||e[0]!==\"value\")&&bA(\"Expected (value) in set {a} function.\",g,c);else{c=cP();if(typeof c!==\"string\")break;bI(\":\"),bN($,K),bJ(10)}f[c]===!0&&bA(\"Duplicate member '{a}'.\",K,c),f[c]=!0,cn(c);if(K.id===\",\")bR(),K.id===\",\"?bA(\"Extra comma.\",$):K.id===\"}\"&&!N.es5&&bA(\"Extra comma.\",$);else break}a&&(D-=N.indent,bP()),bI(\"}\",this);return this},a.fud=function(){bC(\"Expected to see a statement and instead saw a block.\",$)}}(bT(\"{\"));var cS=function cS(a){var b,c,d;v[\"(onevar)\"]&&N.onevar?bA(\"Too many var statements.\"):v[\"(global)\"]||(v[\"(onevar)\"]=!0),this.first=[];for(;;){bN($,K),b=ch(),v[\"(global)\"]&&O[b]===!1&&bA(\"Redefinition of '{a}'.\",$,b),bF(b,\"unused\");if(a)break;c=$,this.first.push($),K.id===\"=\"&&(bN($,K),bI(\"=\"),bN($,K),K.id===\"undefined\"&&bA(\"It is not necessary to initialize '{a}' to 'undefined'.\",$,b),bH(0).id===\"=\"&&K.identifier&&bC(\"Variable {a} was not declared correctly.\",K,K.value),d=bJ(0),c.first=d);if(K.id!==\",\")break;bR()}return this};bU(\"var\",cS).exps=!0,bV(\"function\",function(){C&&bA(\"Function statements should not be placed in blocks. Use a function expression or move the statement to the top of the outer function.\",$);var a=ch();bK($,K),bF(a,\"unction\"),cR(a,!0),K.id===\"(\"&&K.line===$.line&&bC(\"Function statements are not invocable. Wrap the whole function invocation in parens.\");return this}),bX(\"function\",function(){var a=cg();a?bK($,K):bN($,K),cR(a),v[\"(loopage)\"]&&bA(\"Don't make functions within a loop.\");return this}),bV(\"if\",function(){var a=K;bI(\"(\"),bN(this,a),bM(),bJ(20),K.id===\"=\"&&(N.boss||bA(\"Expected a conditional expression and instead saw an assignment.\"),bI(\"=\"),bJ(20)),bI(\")\",a),bM(Q,$),cm(!0,!0),K.id===\"else\"&&(bN($,K),bI(\"else\"),K.id===\"if\"||K.id===\"switch\"?cj(!0):cm(!0,!0));return this}),bV(\"try\",function(){var a,b,c;N.adsafe&&bA(\"ADsafe try violation.\",this),cm(!1),K.id===\"catch\"&&(bI(\"catch\"),bN($,K),bI(\"(\"),c=S,S=Object.create(c),b=K.value,K.type!==\"(identifier)\"?bA(\"Expected an identifier and instead saw '{a}'.\",K,b):bF(b,\"exception\"),bI(),bI(\")\"),cm(!1),a=!0,S=c);if(K.id===\"finally\")bI(\"finally\"),cm(!1);else{a||bC(\"Expected '{a}' and instead saw '{b}'.\",K,\"catch\",K.value);return this}}),bV(\"while\",function(){var a=K;v[\"(breakage)\"]+=1,v[\"(loopage)\"]+=1,bI(\"(\"),bN(this,a),bM(),bJ(20),K.id===\"=\"&&(N.boss||bA(\"Expected a conditional expression and instead saw an assignment.\"),bI(\"=\"),bJ(20)),bI(\")\",a),bM(Q,$),cm(!0,!0),v[\"(breakage)\"]-=1,v[\"(loopage)\"]-=1;return this}).labelled=!0,bZ(\"with\"),bV(\"switch\",function(){var a=K,b=!1;v[\"(breakage)\"]+=1,bI(\"(\"),bN(this,a),bM(),this.condition=bJ(20),bI(\")\",a),bM(Q,$),bN($,K),a=K,bI(\"{\"),bN($,K),D+=N.indent,this.cases=[];for(;;)switch(K.id){case\"case\":switch(v[\"(verb)\"]){case\"break\":case\"case\":case\"continue\":case\"return\":case\"switch\":case\"throw\":break;default:bA(\"Expected a 'break' statement before 'case'.\",$)}bP(-N.indent),bI(\"case\"),this.cases.push(bJ(20)),b=!0,bI(\":\"),v[\"(verb)\"]=\"case\";break;case\"default\":switch(v[\"(verb)\"]){case\"break\":case\"continue\":case\"return\":case\"throw\":break;default:bA(\"Expected a 'break' statement before 'default'.\",$)}bP(-N.indent),bI(\"default\"),b=!0,bI(\":\");break;case\"}\":D-=N.indent,bP(),bI(\"}\",a),(this.cases.length===1||this.condition.id===\"true\"||this.condition.id===\"false\")&&bA(\"This 'switch' should be an 'if'.\",this),v[\"(breakage)\"]-=1,v[\"(verb)\"]=undefined;return;case\"(end)\":bC(\"Missing '{a}'.\",K,\"}\");return;default:if(b)switch($.id){case\",\":bC(\"Each value should have its own case label.\");return;case\":\":cl();break;default:bC(\"Missing ':' on a case clause.\",$)}else bC(\"Expected '{a}' and instead saw '{b}'.\",K,\"case\",K.value)}}).labelled=!0,bU(\"debugger\",function(){N.debug||bA(\"All 'debugger' statements should be removed.\");return this}).exps=!0,function(){var a=bU(\"do\",function(){v[\"(breakage)\"]+=1,v[\"(loopage)\"]+=1,this.first=cm(!0),bI(\"while\");var a=K;bN($,a),bI(\"(\"),bM(),bJ(20),K.id===\"=\"&&(N.boss||bA(\"Expected a conditional expression and instead saw an assignment.\"),bI(\"=\"),bJ(20)),bI(\")\",a),bM(Q,$),v[\"(breakage)\"]-=1,v[\"(loopage)\"]-=1;return this});a.labelled=!0,a.exps=!0}(),bV(\"for\",function(){var a=N.forin,b,c=K;v[\"(breakage)\"]+=1,v[\"(loopage)\"]+=1,bI(\"(\"),bN(this,c),bM();if(bH(K.id===\"var\"?1:0).id===\"in\"){if(K.id===\"var\")bI(\"var\"),cS(!0);else{switch(v[K.value]){case\"unused\":v[K.value]=\"var\";break;case\"var\":break;default:bA(\"Bad for in variable '{a}'.\",K,K.value)}bI()}bI(\"in\"),bJ(20),bI(\")\",c),b=cm(!0,!0),!a&&(b.length>1||typeof b[0]!==\"object\"||b[0].value!==\"if\")&&bA(\"The body of a for in should be wrapped in an if statement to filter unwanted properties from the prototype.\",this),v[\"(breakage)\"]-=1,v[\"(loopage)\"]-=1;return this}if(K.id!==\";\")if(K.id===\"var\")bI(\"var\"),cS();else for(;;){bJ(0,\"for\");if(K.id!==\",\")break;bR()}bQ($),bI(\";\"),K.id!==\";\"&&(bJ(20),K.id===\"=\"&&(N.boss||bA(\"Expected a conditional expression and instead saw an assignment.\"),bI(\"=\"),bJ(20))),bQ($),bI(\";\"),K.id===\";\"&&bC(\"Expected '{a}' and instead saw '{b}'.\",K,\")\",\";\");if(K.id!==\")\")for(;;){bJ(0,\"for\");if(K.id!==\",\")break;bR()}bI(\")\",c),bM(Q,$),cm(!0,!0),v[\"(breakage)\"]-=1,v[\"(loopage)\"]-=1;return this}).labelled=!0,bU(\"break\",function(){var a=K.value;v[\"(breakage)\"]===0&&bA(\"Unexpected '{a}'.\",K,this.value),bQ(this),K.id!==\";\"&&$.line===K.line&&(v[a]!==\"label\"?bA(\"'{a}' is not a statement label.\",K,a):S[a]!==v&&bA(\"'{a}' is out of scope.\",K,a),this.first=K,bI()),ci(\"break\");return this}).exps=!0,bU(\"continue\",function(){var a=K.value;v[\"(breakage)\"]===0&&bA(\"Unexpected '{a}'.\",K,this.value),bQ(this),K.id!==\";\"?$.line===K.line&&(v[a]!==\"label\"?bA(\"'{a}' is not a statement label.\",K,a):S[a]!==v&&bA(\"'{a}' is out of scope.\",K,a),this.first=K,bI()):v[\"(loopage)\"]||bA(\"Unexpected '{a}'.\",K,this.value),ci(\"continue\");return this}).exps=!0,bU(\"return\",function(){bQ(this),K.id===\"(regexp)\"&&bA(\"Wrap the /regexp/ literal in parens to disambiguate the slash operator.\"),K.id!==\";\"&&!K.reach&&(bN($,K),this.first=bJ(20)),ci(\"return\");return this}).exps=!0,bU(\"throw\",function(){bQ(this),bN($,K),this.first=bJ(20),ci(\"throw\");return this}).exps=!0,bZ(\"void\"),bZ(\"class\"),bZ(\"const\"),bZ(\"enum\"),bZ(\"export\"),bZ(\"extends\"),bZ(\"import\"),bZ(\"super\"),bZ(\"let\"),bZ(\"yield\"),bZ(\"implements\"),bZ(\"interface\"),bZ(\"package\"),bZ(\"private\"),bZ(\"protected\"),bZ(\"public\"),bZ(\"static\");var cU=function(e,g){var h,i,j;d.errors=[],O=Object.create(V);if(g){h=g.predef;if(h)if(Array.isArray(h))for(i=0;i<h.length;i+=1)O[h[i]]=!0;else if(typeof h===\"object\"){j=Object.keys(h);for(i=0;i<j.length;i+=1)O[j[i]]=!!h[j]}g.adsafe&&(g.safe=!0),g.safe&&(g.browser=g.css=g.debug=g.devel=g.evil=g.forin=g.on=g.rhino=g.windows=g.sub=g.widget=!1,g.eqeqeq=g.nomen=g.safe=g.undef=!0,O.Date=O.eval=O.Function=O.Object=null,O.ADSAFE=O.lib=!1),N=g}else N={};N.indent=N.indent||4,N.maxerr=N.maxerr||50,a=\"\",b=!1,c=!1,f={};if(N.approved)for(i=0;i<N.approved.length;i+=1)f[N.approved[i]]=N.approved[i];else f.test=\"test\";Z=\"\";for(i=0;i<N.indent;i+=1)Z+=\" \";D=1,y=Object.create(O),S=y,v={\"(global)\":!0,\"(name)\":\"(global)\",\"(scope)\":S,\"(breakage)\":0,\"(loopage)\":0},x=[v],A={},_=[],T=!1,bd=!1,U=null,I={},J=null,B={},C=!1,H=[],E=!1,ba=0,bE.init(e),P=!0,X=!1,Q=$=K=Y[\"(begin)\"],by();try{bI();if(K.value.charAt(0)===\"<\")cO(),N.adsafe&&!c&&bA(\"ADsafe violation: Missing ADSAFE.go.\",this);else switch(K.id){case\"{\":case\"[\":N.laxbreak=!0,E=!0,cT();break;case\"@\":case\"*\":case\"#\":case\".\":case\":\":bd=\"style\",bI(),($.id!==\"@\"||!K.identifier||K.value!==\"charset\"||$.line!==1||$.from!==1)&&bC(\"A css file should begin with @charset 'UTF-8';\"),bI(),K.type!==\"(string)\"&&K.value!==\"UTF-8\"&&bC(\"A css file should begin with @charset 'UTF-8';\"),bI(),bI(\";\"),cJ();break;default:N.adsafe&&N.fragment&&bC(\"Expected '{a}' and instead saw '{b}'.\",K,\"<div>\",K.value),K.value===\"use strict\"&&(bA('Use the function form of \"use strict\".'),ck()),cl(\"lib\")}bI(\"(end)\")}catch(k){k&&d.errors.push({reason:k.message,line:k.line||K.line,character:k.character||K.from},null)}return d.errors.length===0};cU.data=function(){var a={functions:[]},b,c,d=[],e,f,g,h=[],i,j=[],k;cU.errors.length&&(a.errors=cU.errors),E&&(a.json=!0);for(i in B)bw(B,i)&&d.push({name:i,line:B[i]});d.length>0&&(a.implieds=d),_.length>0&&(a.urls=_),c=Object.keys(S),c.length>0&&(a.globals=c);for(f=1;f<x.length;f+=1){e=x[f],b={};for(g=0;g<w.length;g+=1)b[w[g]]=[];for(i in e)bw(e,i)&&i.charAt(0)!==\"(\"&&(k=e[i],k===\"unction\"&&(k=\"unused\"),Array.isArray(b[k])&&(b[k].push(i),k===\"unused\"&&j.push({name:i,line:e[\"(line)\"],\"function\":e[\"(name)\"]})));for(g=0;g<w.length;g+=1)b[w[g]].length===0&&delete b[w[g]];b.name=e[\"(name)\"],b.param=e[\"(params)\"],b.line=e[\"(line)\"],b.last=e[\"(last)\"],a.functions.push(b)}j.length>0&&(a.unused=j),h=[];for(i in I)if(typeof I[i]===\"number\"){a.member=I;break}return a},cU.report=function(a){function o(a,b){var c,d,e;if(b){m.push(\"<div><i>\"+a+\"</i> \"),b=b.sort();for(d=0;d<b.length;d+=1)b[d]!==e&&(e=b[d],m.push((c?\", \":\"\")+e),c=!0);m.push(\"</div>\")}}var b=cU.data(),c=[],d,e,f,g,h,i,j,k=\"\",l,m=[],n;if(b.errors||b.implieds||b.unused){f=!0,m.push(\"<div id=errors><i>Error:</i>\");if(b.errors)for(h=0;h<b.errors.length;h+=1)d=b.errors[h],d&&(e=d.evidence||\"\",m.push(\"<p>Problem\"+(isFinite(d.line)?\" at line \"+d.line+\" character \"+d.character:\"\")+\": \"+d.reason.entityify()+\"</p><p class=evidence>\"+(e&&(e.length>80?e.slice(0,77)+\"...\":e).entityify())+\"</p>\"));if(b.implieds){n=[];for(h=0;h<b.implieds.length;h+=1)n[h]=\"<code>\"+b.implieds[h].name+\"</code>&nbsp;<i>\"+b.implieds[h].line+\"</i>\";m.push(\"<p><i>Implied global:</i> \"+n.join(\", \")+\"</p>\")}if(b.unused){n=[];for(h=0;h<b.unused.length;h+=1)n[h]=\"<code><u>\"+b.unused[h].name+\"</u></code>&nbsp;<i>\"+b.unused[h].line+\"</i> <code>\"+b.unused[h][\"function\"]+\"</code>\";m.push(\"<p><i>Unused variable:</i> \"+n.join(\", \")+\"</p>\")}b.json&&m.push(\"<p>JSON: bad.</p>\"),m.push(\"</div>\")}if(!a){m.push(\"<br><div id=functions>\"),b.urls&&o(\"URLs<br>\",b.urls,\"<br>\"),bd===\"style\"?m.push(\"<p>CSS.</p>\"):b.json&&!f?m.push(\"<p>JSON: good.</p>\"):b.globals?m.push(\"<div><i>Global</i> \"+b.globals.sort().join(\", \")+\"</div>\"):m.push(\"<div><i>No new global variables introduced.</i></div>\");for(h=0;h<b.functions.length;h+=1)g=b.functions[h],m.push(\"<br><div class=function><i>\"+g.line+\"-\"+g.last+\"</i> \"+(g.name||\"\")+\"(\"+(g.param?g.param.join(\", \"):\"\")+\")</div>\"),o(\"<big><b>Unused</b></big>\",g.unused),o(\"Closure\",g.closure),o(\"Variable\",g[\"var\"]),o(\"Exception\",g.exception),o(\"Outer\",g.outer),o(\"Global\",g.global),o(\"Label\",g.label);if(b.member){c=Object.keys(b.member);if(c.length){c=c.sort(),k=\"<br><pre id=members>/*members \",j=10;for(h=0;h<c.length;h+=1)i=c[h],l=i.name(),j+l.length>72&&(m.push(k+\"<br>\"),k=\"    \",j=1),j+=l.length+2,b.member[i]===1&&(l=\"<i>\"+l+\"</i>\"),h<c.length-1&&(l+=\", \"),k+=l;m.push(k+\"<br>*/</pre>\")}m.push(\"</div>\")}}return m.join(\"\")},cU.jshint=cU,cU.edition=\"2011-02-19\";return cU}();typeof b==\"object\"&&b&&(b.JSHINT=d)}),define(\"ace/narcissus/jsparse\",function(require,exports,module){function parseStdin(a,b){for(;;)try{var c=new lexer.Tokenizer(a,\"stdin\",b.value),d=Script(c,!1);b.value=c.lineno;return d}catch(e){if(!c.unexpectedEOF)throw e;var f=readline();if(!f)throw e;a+=\"\\n\"+f}}function parse(a,b,c){var d=new lexer.Tokenizer(a,b,c),e=Script(d,!1);if(!d.done)throw d.newSyntaxError(\"Syntax error\");return e}function PrimaryExpression(a,b){var c,d,e=a.get(!0);switch(e){case FUNCTION:c=FunctionDefinition(a,b,!1,EXPRESSED_FORM);break;case LEFT_BRACKET:c=new Node(a,{type:ARRAY_INIT});while((e=a.peek(!0))!==RIGHT_BRACKET){if(e===COMMA){a.get(),c.push(null);continue}c.push(AssignExpression(a,b));if(e!==COMMA&&!a.match(COMMA))break}c.children.length===1&&a.match(FOR)&&(d=new Node(a,{type:ARRAY_COMP,expression:c.children[0],tail:ComprehensionTail(a,b)}),c=d),a.mustMatch(RIGHT_BRACKET);break;case LEFT_CURLY:var f,g;c=new Node(a,{type:OBJECT_INIT});object_init:if(!a.match(RIGHT_CURLY)){do{e=a.get();if(a.token.value!==\"get\"&&a.token.value!==\"set\"||a.peek()!==IDENTIFIER){switch(e){case IDENTIFIER:case NUMBER:case STRING:f=new Node(a,{type:IDENTIFIER});break;case RIGHT_CURLY:if(b.ecma3OnlyMode)throw a.newSyntaxError(\"Illegal trailing ,\");break object_init;default:if(a.token.value in definitions.keywords){f=new Node(a,{type:IDENTIFIER});break}throw a.newSyntaxError(\"Invalid property name\")}if(a.match(COLON))d=new Node(a,{type:PROPERTY_INIT}),d.push(f),d.push(AssignExpression(a,b)),c.push(d);else{if(a.peek()!==COMMA&&a.peek()!==RIGHT_CURLY)throw a.newSyntaxError(\"missing : after property\");c.push(f)}}else{if(b.ecma3OnlyMode)throw a.newSyntaxError(\"Illegal property accessor\");c.push(FunctionDefinition(a,b,!0,EXPRESSED_FORM))}}while(a.match(COMMA));a.mustMatch(RIGHT_CURLY)}break;case LEFT_PAREN:c=ParenExpression(a,b),a.mustMatch(RIGHT_PAREN),c.parenthesized=!0;break;case LET:c=LetBlock(a,b,!1);break;case NULL:case THIS:case TRUE:case FALSE:case IDENTIFIER:case NUMBER:case STRING:case REGEXP:c=new Node(a);break;default:throw a.newSyntaxError(\"missing operand\")}return c}function ArgumentList(a,b){var c,d;c=new Node(a,{type:LIST});if(a.match(RIGHT_PAREN,!0))return c;do{d=AssignExpression(a,b);if(d.type===YIELD&&!d.parenthesized&&a.peek()===COMMA)throw a.newSyntaxError(\"Yield expression must be parenthesized\");if(a.match(FOR)){d=GeneratorExpression(a,b,d);if(c.children.length>1||a.peek(!0)===COMMA)throw a.newSyntaxError(\"Generator expression must be parenthesized\")}c.push(d)}while(a.match(COMMA));a.mustMatch(RIGHT_PAREN);return c}function MemberExpression(a,b,c){var d,e,f,g;a.match(NEW)?(d=new Node(a),d.push(MemberExpression(a,b,!1)),a.match(LEFT_PAREN)&&(d.type=NEW_WITH_ARGS,d.push(ArgumentList(a,b)))):d=PrimaryExpression(a,b);while((g=a.get())!==END){switch(g){case DOT:e=new Node(a),e.push(d),a.mustMatch(IDENTIFIER),e.push(new Node(a));break;case LEFT_BRACKET:e=new Node(a,{type:INDEX}),e.push(d),e.push(Expression(a,b)),a.mustMatch(RIGHT_BRACKET);break;case LEFT_PAREN:if(c){e=new Node(a,{type:CALL}),e.push(d),e.push(ArgumentList(a,b));break};default:a.unget();return d}d=e}return d}function UnaryExpression(a,b){var c,d,e;switch(e=a.get(!0)){case DELETE:case VOID:case TYPEOF:case NOT:case BITWISE_NOT:case PLUS:case MINUS:e===PLUS?c=new Node(a,{type:UNARY_PLUS}):e===MINUS?c=new Node(a,{type:UNARY_MINUS}):c=new Node(a),c.push(UnaryExpression(a,b));break;case INCREMENT:case DECREMENT:c=new Node(a),c.push(MemberExpression(a,b,!0));break;default:a.unget(),c=MemberExpression(a,b,!0),a.tokens[a.tokenIndex+a.lookahead-1&3].lineno===a.lineno&&(a.match(INCREMENT)||a.match(DECREMENT))&&(d=new Node(a,{postfix:!0}),d.push(c),c=d)}return c}function MultiplyExpression(a,b){var c,d;c=UnaryExpression(a,b);while(a.match(MUL)||a.match(DIV)||a.match(MOD))d=new Node(a),d.push(c),d.push(UnaryExpression(a,b)),c=d;return c}function AddExpression(a,b){var c,d;c=MultiplyExpression(a,b);while(a.match(PLUS)||a.match(MINUS))d=new Node(a),d.push(c),d.push(MultiplyExpression(a,b)),c=d;return c}function ShiftExpression(a,b){var c,d;c=AddExpression(a,b);while(a.match(LSH)||a.match(RSH)||a.match(URSH))d=new Node(a),d.push(c),d.push(AddExpression(a,b)),c=d;return c}function RelationalExpression(a,b){var c,d,e=b.update({inForLoopInit:!1});c=ShiftExpression(a,e);while(a.match(LT)||a.match(LE)||a.match(GE)||a.match(GT)||!b.inForLoopInit&&a.match(IN)||a.match(INSTANCEOF))d=new Node(a),d.push(c),d.push(ShiftExpression(a,e)),c=d;return c}function EqualityExpression(a,b){var c,d;c=RelationalExpression(a,b);while(a.match(EQ)||a.match(NE)||a.match(STRICT_EQ)||a.match(STRICT_NE))d=new Node(a),d.push(c),d.push(RelationalExpression(a,b)),c=d;return c}function BitwiseAndExpression(a,b){var c,d;c=EqualityExpression(a,b);while(a.match(BITWISE_AND))d=new Node(a),d.push(c),d.push(EqualityExpression(a,b)),c=d;return c}function BitwiseXorExpression(a,b){var c,d;c=BitwiseAndExpression(a,b);while(a.match(BITWISE_XOR))d=new Node(a),d.push(c),d.push(BitwiseAndExpression(a,b)),c=d;return c}function BitwiseOrExpression(a,b){var c,d;c=BitwiseXorExpression(a,b);while(a.match(BITWISE_OR))d=new Node(a),d.push(c),d.push(BitwiseXorExpression(a,b)),c=d;return c}function AndExpression(a,b){var c,d;c=BitwiseOrExpression(a,b);while(a.match(AND))d=new Node(a),d.push(c),d.push(BitwiseOrExpression(a,b)),c=d;return c}function OrExpression(a,b){var c,d;c=AndExpression(a,b);while(a.match(OR))d=new Node(a),d.push(c),d.push(AndExpression(a,b)),c=d;return c}function ConditionalExpression(a,b){var c,d;c=OrExpression(a,b);if(a.match(HOOK)){d=c,c=new Node(a,{type:HOOK}),c.push(d),c.push(AssignExpression(a,b.update({inForLoopInit:!1})));if(!a.match(COLON))throw a.newSyntaxError(\"missing : after ?\");c.push(AssignExpression(a,b))}return c}function AssignExpression(a,b){var c,d;if(a.match(YIELD,!0))return ReturnOrYield(a,b);c=new Node(a,{type:ASSIGN}),d=ConditionalExpression(a,b);if(!a.match(ASSIGN))return d;switch(d.type){case OBJECT_INIT:case ARRAY_INIT:d.destructuredNames=checkDestructuring(a,b,d);case IDENTIFIER:case DOT:case INDEX:case CALL:break;default:throw a.newSyntaxError(\"Bad left-hand side of assignment\")}c.assignOp=a.token.assignOp,c.push(d),c.push(AssignExpression(a,b));return c}function Expression(a,b){var c,d;c=AssignExpression(a,b);if(a.match(COMMA)){d=new Node(a,{type:COMMA}),d.push(c),c=d;do{d=c.children[c.children.length-1];if(d.type===YIELD&&!d.parenthesized)throw a.newSyntaxError(\"Yield expression must be parenthesized\");c.push(AssignExpression(a,b))}while(a.match(COMMA))}return c}function ParenExpression(a,b){var c=Expression(a,b.update({inForLoopInit:b.inForLoopInit&&a.token.type===LEFT_PAREN}));if(a.match(FOR)){if(c.type===YIELD&&!c.parenthesized)throw a.newSyntaxError(\"Yield expression must be parenthesized\");if(c.type===COMMA&&!c.parenthesized)throw a.newSyntaxError(\"Generator expression must be parenthesized\");c=GeneratorExpression(a,b,c)}return c}function HeadExpression(a,b){var c=MaybeLeftParen(a,b),d=ParenExpression(a,b);MaybeRightParen(a,c);if(c===END&&!d.parenthesized){var e=a.peek();if(e!==LEFT_CURLY&&!definitions.isStatementStartCode[e])throw a.newSyntaxError(\"Unparenthesized head followed by unbraced body\")}return d}function ComprehensionTail(a,b){var c,d,e,f,g;c=new Node(a,{type:COMP_TAIL});do{d=new Node(a,{type:FOR_IN,isLoop:!0}),a.match(IDENTIFIER)&&(a.token.value===\"each\"?d.isEach=!0:a.unget()),g=MaybeLeftParen(a,b);switch(a.get()){case LEFT_BRACKET:case LEFT_CURLY:a.unget(),d.iterator=DestructuringExpression(a,b);break;case IDENTIFIER:d.iterator=f=new Node(a,{type:IDENTIFIER}),f.name=f.value,d.varDecl=e=new Node(a,{type:VAR}),e.push(f),b.parentScript.varDecls.push(f);break;default:throw a.newSyntaxError(\"missing identifier\")}a.mustMatch(IN),d.object=Expression(a,b),MaybeRightParen(a,g),c.push(d)}while(a.match(FOR));a.match(IF)&&(c.guard=HeadExpression(a,b));return c}function GeneratorExpression(a,b,c){return new Node(a,{type:GENERATOR,expression:c,tail:ComprehensionTail(a,b)})}function DestructuringExpression(a,b,c){var d=PrimaryExpression(a,b);d.destructuredNames=checkDestructuring(a,b,d,c);return d}function checkDestructuring(a,b,c,d){if(c.type===ARRAY_COMP)throw a.newSyntaxError(\"Invalid array comprehension left-hand side\");if(c.type===ARRAY_INIT||c.type===OBJECT_INIT){var e={},f,g,h,i,j,k=c.children;for(var l=0,m=k.length;l<m;l++){if(!(f=k[l]))continue;f.type===PROPERTY_INIT?(j=f.children,i=j[1],h=j[0].value):c.type===OBJECT_INIT?(i=f,h=f.value):(i=f,h=l);if(i.type===ARRAY_INIT||i.type===OBJECT_INIT)e[h]=checkDestructuring(a,b,i,d);else{if(d&&i.type!==IDENTIFIER)throw a.newSyntaxError(\"missing name in pattern\");e[h]=i}}return e}}function LetBlock(a,b,c){var d,e;d=new Node(a,{type:LET_BLOCK,varDecls:[]}),a.mustMatch(LEFT_PAREN),d.variables=Variables(a,b,d),a.mustMatch(RIGHT_PAREN),c&&a.peek()!==LEFT_CURLY&&(e=new Node(a,{type:SEMICOLON,expression:d}),c=!1),c?d.block=Block(a,b):d.expression=AssignExpression(a,b);return d}function Variables(a,b,c){var d,e,f,g,h,i;i=a.token.type;switch(i){case VAR:case CONST:h=b.parentScript;break;case LET:h=b.parentBlock;break;case LEFT_PAREN:i=LET,h=c}d=new Node(a,{type:i,destructurings:[]});do{i=a.get();if(i===LEFT_BRACKET||i===LEFT_CURLY){a.unget();var j=DestructuringExpression(a,b,!0);e=new Node(a,{type:IDENTIFIER,name:j,readOnly:d.type===CONST}),d.push(e),pushDestructuringVarDecls(e.name.destructuredNames,h),d.destructurings.push({exp:j,decl:e});if(b.inForLoopInit&&a.peek()===IN)continue;a.mustMatch(ASSIGN);if(a.token.assignOp)throw a.newSyntaxError(\"Invalid variable initialization\");e.initializer=AssignExpression(a,b);continue}if(i!==IDENTIFIER)throw a.newSyntaxError(\"missing variable name\");e=new Node(a,{type:IDENTIFIER,name:a.token.value,readOnly:d.type===CONST}),d.push(e),h.varDecls.push(e);if(a.match(ASSIGN)){if(a.token.assignOp)throw a.newSyntaxError(\"Invalid variable initialization\");e.initializer=AssignExpression(a,b)}}while(a.match(COMMA));return d}function FunctionDefinition(a,b,c,d){var e,f=new Node(a,{params:[]});f.type!==FUNCTION&&(f.type=f.value===\"get\"?GETTER:SETTER);if(a.match(IDENTIFIER))f.name=a.token.value;else if(c)throw a.newSyntaxError(\"missing function identifier\");var g=new StaticContext(null,null,!0,!1,NESTING_TOP);a.mustMatch(LEFT_PAREN);if(!a.match(RIGHT_PAREN)){do switch(a.get()){case LEFT_BRACKET:case LEFT_CURLY:a.unget(),f.params.push(DestructuringExpression(a,g));break;case IDENTIFIER:f.params.push(a.token.value);break;default:throw a.newSyntaxError(\"missing formal parameter\")}while(a.match(COMMA));a.mustMatch(RIGHT_PAREN)}e=a.get(),e!==LEFT_CURLY&&a.unget();if(e!==LEFT_CURLY){f.body=AssignExpression(a,g);if(f.body.isGenerator)throw a.newSyntaxError(\"Generator returns a value\")}else f.body=Script(a,!0);e===LEFT_CURLY&&a.mustMatch(RIGHT_CURLY),f.end=a.token.end,f.functionForm=d,d===DECLARED_FORM&&b.parentScript.funDecls.push(f);return f}function ReturnOrYield(a,b){var c,d,e=a.token.type,f,g=b.parentScript;if(e===RETURN){if(!b.inFunction)throw a.newSyntaxError(\"Return not in function\")}else{if(!b.inFunction)throw a.newSyntaxError(\"Yield not in function\");g.isGenerator=!0}c=new Node(a,{value:undefined}),f=a.peek(!0),f!==END&&f!==NEWLINE&&f!==SEMICOLON&&f!==RIGHT_CURLY&&(e!==YIELD||f!==e&&f!==RIGHT_BRACKET&&f!==RIGHT_PAREN&&f!==COLON&&f!==COMMA)?e===RETURN?(c.value=Expression(a,b),g.hasReturnWithValue=!0):c.value=AssignExpression(a,b):e===RETURN&&(g.hasEmptyReturn=!0);if(g.hasReturnWithValue&&g.isGenerator)throw a.newSyntaxError(\"Generator returns a value\");return c}function MagicalSemicolon(a){var b;if(a.lineno===a.token.lineno){b=a.peekOnSameLine();if(b!==END&&b!==NEWLINE&&b!==SEMICOLON&&b!==RIGHT_CURLY)throw a.newSyntaxError(\"missing ; before statement\")}a.match(SEMICOLON)}function Statement(a,b){var c,d,e,f,g,h,i,j=a.get(!0),k,l,m;switch(j){case FUNCTION:return FunctionDefinition(a,b,!0,b.nesting!==NESTING_TOP?STATEMENT_FORM:DECLARED_FORM);case LEFT_CURLY:e=new Node(a,blockInit()),Statements(a,b.update({parentBlock:e}).pushTarget(e).nest(NESTING_SHALLOW),e),a.mustMatch(RIGHT_CURLY);return e;case IF:e=new Node(a),e.condition=HeadExpression(a,b),l=b.pushTarget(e).nest(NESTING_DEEP),e.thenPart=Statement(a,l),e.elsePart=a.match(ELSE)?Statement(a,l):null;return e;case SWITCH:e=new Node(a,{cases:[],defaultIndex:-1}),e.discriminant=HeadExpression(a,b),l=b.pushTarget(e).nest(NESTING_DEEP),a.mustMatch(LEFT_CURLY);while((j=a.get())!==RIGHT_CURLY){switch(j){case DEFAULT:if(e.defaultIndex>=0)throw a.newSyntaxError(\"More than one switch default\");case CASE:f=new Node(a),j===DEFAULT?e.defaultIndex=e.cases.length:f.caseLabel=Expression(a,l,COLON);break;default:throw a.newSyntaxError(\"Invalid switch case\")}a.mustMatch(COLON),f.statements=new Node(a,blockInit());while((j=a.peek(!0))!==CASE&&j!==DEFAULT&&j!==RIGHT_CURLY)f.statements.push(Statement(a,l));e.cases.push(f)}return e;case FOR:e=new Node(a,LOOP_INIT),a.match(IDENTIFIER)&&(a.token.value===\"each\"?e.isEach=!0:a.unget()),b.parenFreeMode||a.mustMatch(LEFT_PAREN),l=b.pushTarget(e).nest(NESTING_DEEP),m=b.update({inForLoopInit:!0}),(j=a.peek())!==SEMICOLON&&(j===VAR||j===CONST?(a.get(),f=Variables(a,m)):j===LET?(a.get(),a.peek()===LEFT_PAREN?f=LetBlock(a,m,!1):(m.parentBlock=e,e.varDecls=[],f=Variables(a,m))):f=Expression(a,m));if(f&&a.match(IN)){e.type=FOR_IN,e.object=Expression(a,m);if(f.type===VAR||f.type===LET){h=f.children;if(h.length!==1&&f.destructurings.length!==1)throw new SyntaxError(\"Invalid for..in left-hand side\",a.filename,f.lineno);f.destructurings.length>0?e.iterator=f.destructurings[0]:e.iterator=h[0],e.varDecl=f}else{if(f.type===ARRAY_INIT||f.type===OBJECT_INIT)f.destructuredNames=checkDestructuring(a,m,f);e.iterator=f}}else{e.setup=f,a.mustMatch(SEMICOLON);if(e.isEach)throw a.newSyntaxError(\"Invalid for each..in loop\");e.condition=a.peek()===SEMICOLON?null:Expression(a,m),a.mustMatch(SEMICOLON),k=a.peek(),e.update=(b.parenFreeMode?k===LEFT_CURLY||definitions.isStatementStartCode[k]:k===RIGHT_PAREN)?null:Expression(a,m)}b.parenFreeMode||a.mustMatch(RIGHT_PAREN),e.body=Statement(a,l);return e;case WHILE:e=new Node(a,{isLoop:!0}),e.condition=HeadExpression(a,b),e.body=Statement(a,b.pushTarget(e).nest(NESTING_DEEP));return e;case DO:e=new Node(a,{isLoop:!0}),e.body=Statement(a,b.pushTarget(e).nest(NESTING_DEEP)),a.mustMatch(WHILE),e.condition=HeadExpression(a,b);if(!b.ecmaStrictMode){a.match(SEMICOLON);return e}break;case BREAK:case CONTINUE:e=new Node(a),l=b.pushTarget(e),a.peekOnSameLine()===IDENTIFIER&&(a.get(),e.label=a.token.value),e.target=e.label?l.labeledTargets.find(function(a){return a.labels.has(e.label)}):l.defaultTarget;if(!e.target)throw a.newSyntaxError(\"Invalid \"+(j===BREAK?\"break\":\"continue\"));if(!e.target.isLoop&&j===CONTINUE)throw a.newSyntaxError(\"Invalid continue\");break;case TRY:e=new Node(a,{catchClauses:[]}),e.tryBlock=Block(a,b);while(a.match(CATCH)){f=new Node(a),g=MaybeLeftParen(a,b);switch(a.get()){case LEFT_BRACKET:case LEFT_CURLY:a.unget(),f.varName=DestructuringExpression(a,b,!0);break;case IDENTIFIER:f.varName=a.token.value;break;default:throw a.newSyntaxError(\"missing identifier in catch\")}if(a.match(IF)){if(b.ecma3OnlyMode)throw a.newSyntaxError(\"Illegal catch guard\");if(e.catchClauses.length&&!e.catchClauses.top().guard)throw a.newSyntaxError(\"Guarded catch after unguarded\");f.guard=Expression(a,b)}MaybeRightParen(a,g),f.block=Block(a,b),e.catchClauses.push(f)}a.match(FINALLY)&&(e.finallyBlock=Block(a,b));if(!e.catchClauses.length&&!e.finallyBlock)throw a.newSyntaxError(\"Invalid try statement\");return e;case CATCH:case FINALLY:throw a.newSyntaxError(definitions.tokens[j]+\" without preceding try\");case THROW:e=new Node(a),e.exception=Expression(a,b);break;case RETURN:e=ReturnOrYield(a,b);break;case WITH:e=new Node(a),e.object=HeadExpression(a,b),e.body=Statement(a,b.pushTarget(e).nest(NESTING_DEEP));return e;case VAR:case CONST:e=Variables(a,b);break;case LET:a.peek()===LEFT_PAREN?e=LetBlock(a,b,!0):e=Variables(a,b);break;case DEBUGGER:e=new Node(a);break;case NEWLINE:case SEMICOLON:e=new Node(a,{type:SEMICOLON}),e.expression=null;return e;default:if(j===IDENTIFIER){j=a.peek();if(j===COLON){d=a.token.value;if(b.allLabels.has(d))throw a.newSyntaxError(\"Duplicate label\");a.get(),e=new Node(a,{type:LABEL,label:d}),e.statement=Statement(a,b.pushLabel(d).nest(NESTING_SHALLOW)),e.target=e.statement.type===LABEL?e.statement.target:e.statement;return e}}e=new Node(a,{type:SEMICOLON}),a.unget(),e.expression=Expression(a,b),e.end=e.expression.end}MagicalSemicolon(a);return e}function Block(a,b){a.mustMatch(LEFT_CURLY);var c=new Node(a,blockInit());Statements(a,b.update({parentBlock:c}).pushTarget(c),c),a.mustMatch(RIGHT_CURLY);return c}function Statements(a,b,c){try{while(!a.done&&a.peek(!0)!==RIGHT_CURLY)c.push(Statement(a,b))}catch(d){a.done&&(a.unexpectedEOF=!0);throw d}}function MaybeRightParen(a,b){b===LEFT_PAREN&&a.mustMatch(RIGHT_PAREN)}function MaybeLeftParen(a,b){if(b.parenFreeMode)return a.match(LEFT_PAREN)?LEFT_PAREN:END;return a.mustMatch(LEFT_PAREN).type}function scriptInit(){return{type:SCRIPT,funDecls:[],varDecls:[],modDecls:[],impDecls:[],expDecls:[],loadDeps:[],hasEmptyReturn:!1,hasReturnWithValue:!1,isGenerator:!1}}function blockInit(){return{type:BLOCK,varDecls:[]}}function tokenString(a){var b=definitions.tokens[a];return/^\\W/.test(b)?definitions.opTypeNames[b]:b.toUpperCase()}function Node(a,b){var c=a.token;c?(this.type=c.type,this.value=c.value,this.lineno=c.lineno,this.start=c.start,this.end=c.end):this.lineno=a.lineno,this.tokenizer=a,this.children=[];for(var d in b)this[d]=b[d]}function Script(a,b){var c=new Node(a,scriptInit()),d=new StaticContext(c,c,b,!1,NESTING_TOP);Statements(a,d,c);return c}function StaticContext(a,b,c,d,e){this.parentScript=a,this.parentBlock=b,this.inFunction=c,this.inForLoopInit=d,this.nesting=e,this.allLabels=new Stack,this.currentLabels=new Stack,this.labeledTargets=new Stack,this.defaultTarget=null,definitions.options.ecma3OnlyMode&&(this.ecma3OnlyMode=!0),definitions.options.parenFreeMode&&(this.parenFreeMode=!0)}function pushDestructuringVarDecls(a,b){for(var c in a){var d=a[c];d.type===IDENTIFIER?b.varDecls.push(d):pushDestructuringVarDecls(d,b)}}var lexer=require(\"ace/narcissus/jslex\"),definitions=require(\"ace/narcissus/jsdefs\");const StringMap=definitions.StringMap,Stack=definitions.Stack;eval(definitions.consts);const NESTING_TOP=0,NESTING_SHALLOW=1,NESTING_DEEP=2;StaticContext.prototype={ecma3OnlyMode:!1,parenFreeMode:!1,update:function(a){var b={};for(var c in a)b[c]={value:a[c],writable:!0,enumerable:!0,configurable:!0};return Object.create(this,b)},pushLabel:function(a){return this.update({currentLabels:this.currentLabels.push(a),allLabels:this.allLabels.push(a)})},pushTarget:function(a){var b=a.isLoop||a.type===SWITCH;if(this.currentLabels.isEmpty())return b?this.update({defaultTarget:a}):this;a.labels=new StringMap,this.currentLabels.forEach(function(b){a.labels.set(b,!0)});return this.update({currentLabels:new Stack,labeledTargets:this.labeledTargets.push(a),defaultTarget:b?a:this.defaultTarget})},nest:function(a){var b=Math.max(this.nesting,a);return b!==this.nesting?this.update({nesting:b}):this}},definitions.defineProperty(Array.prototype,\"top\",function(){return this.length&&this[this.length-1]},!1,!1,!0);var Np=Node.prototype={};Np.constructor=Node,Np.toSource=Object.prototype.toSource,Np.push=function(a){a!==null&&(a.start<this.start&&(this.start=a.start),this.end<a.end&&(this.end=a.end));return this.children.push(a)},Node.indentLevel=0,Np.toString=function(){var a=[];for(var b in this)this.hasOwnProperty(b)&&b!==\"type\"&&b!==\"target\"&&a.push({id:b,value:this[b]});a.sort(function(a,b){return a.id<b.id?-1:1});const c=\"    \";var d=++Node.indentLevel,e=\"{\\n\"+c.repeat(d)+\"type: \"+tokenString(this.type);for(b=0;b<a.length;b++)e+=\",\\n\"+c.repeat(d)+a[b].id+\": \"+a[b].value;d=--Node.indentLevel,e+=\"\\n\"+c.repeat(d)+\"}\";return e},Np.getSource=function(){return this.tokenizer.source.slice(this.start,this.end)};const LOOP_INIT={isLoop:!0};definitions.defineGetter(Np,\"filename\",function(){return this.tokenizer.filename}),definitions.defineProperty(String.prototype,\"repeat\",function(a){var b=\"\",c=this+b;while(--a>=0)b+=c;return b},!1,!1,!0);const DECLARED_FORM=0,EXPRESSED_FORM=1,STATEMENT_FORM=2;exports.parse=parse,exports.parseStdin=parseStdin,exports.Node=Node,exports.DECLARED_FORM=DECLARED_FORM,exports.EXPRESSED_FORM=EXPRESSED_FORM,exports.STATEMENT_FORM=STATEMENT_FORM,exports.Tokenizer=lexer.Tokenizer,exports.FunctionDefinition=FunctionDefinition}),define(\"ace/narcissus/jslex\",function(require,exports,module){function Tokenizer(a,b,c){this.cursor=0,this.source=String(a),this.tokens=[],this.tokenIndex=0,this.lookahead=0,this.scanNewlines=!1,this.unexpectedEOF=!1,this.filename=b||\"\",this.lineno=c||1}var definitions=require(\"ace/narcissus/jsdefs\");eval(definitions.consts);var opTokens={};for(var op in definitions.opTypeNames){if(op===\"\\n\"||op===\".\")continue;var node=opTokens;for(var i=0;i<op.length;i++){var ch=op[i];ch in node||(node[ch]={}),node=node[ch],node.op=op}}Tokenizer.prototype={get done(){return this.peek(!0)===END},get token(){return this.tokens[this.tokenIndex]},match:function(a,b){return this.get(b)===a||this.unget()},mustMatch:function(a){if(!this.match(a))throw this.newSyntaxError(\"Missing \"+definitions.tokens[a].toLowerCase());return this.token},peek:function(a){var b,c;this.lookahead?(c=this.tokens[this.tokenIndex+this.lookahead&3],b=this.scanNewlines&&c.lineno!==this.lineno?NEWLINE:c.type):(b=this.get(a),this.unget());return b},peekOnSameLine:function(a){this.scanNewlines=!0;var b=this.peek(a);this.scanNewlines=!1;return b},skip:function(){var a=this.source;for(;;){var b=a[this.cursor++],c=a[this.cursor];if(b===\"\\n\"&&!this.scanNewlines)this.lineno++;else if(b===\"/\"&&c===\"*\"){this.cursor++;for(;;){b=a[this.cursor++];if(b===undefined)throw this.newSyntaxError(\"Unterminated comment\");if(b===\"*\"){c=a[this.cursor];if(c===\"/\"){this.cursor++;break}}else b===\"\\n\"&&this.lineno++}}else if(b===\"/\"&&c===\"/\"){this.cursor++;for(;;){b=a[this.cursor++];if(b===undefined)return;if(b===\"\\n\"){this.lineno++;break}}}else if(b!==\" \"&&b!==\"\\t\"){this.cursor--;return}}},lexExponent:function(){var a=this.source,b=a[this.cursor];if(b===\"e\"||b===\"E\"){this.cursor++,ch=a[this.cursor++];if(ch===\"+\"||ch===\"-\")ch=a[this.cursor++];if(ch<\"0\"||ch>\"9\")throw this.newSyntaxError(\"Missing exponent\");do ch=a[this.cursor++];while(ch>=\"0\"&&ch<=\"9\");this.cursor--;return!0}return!1},lexZeroNumber:function(a){var b=this.token,c=this.source;b.type=NUMBER,a=c[this.cursor++];if(a===\".\"){do a=c[this.cursor++];while(a>=\"0\"&&a<=\"9\");this.cursor--,this.lexExponent(),b.value=parseFloat(b.start,this.cursor)}else if(a===\"x\"||a===\"X\"){do a=c[this.cursor++];while(a>=\"0\"&&a<=\"9\"||a>=\"a\"&&a<=\"f\"||a>=\"A\"&&a<=\"F\");this.cursor--,b.value=parseInt(c.substring(b.start,this.cursor))}else if(a>=\"0\"&&a<=\"7\"){do a=c[this.cursor++];while(a>=\"0\"&&a<=\"7\");this.cursor--,b.value=parseInt(c.substring(b.start,this.cursor))}else this.cursor--,this.lexExponent(),b.value=0},lexNumber:function(a){var b=this.token,c=this.source;b.type=NUMBER;var d=!1;do a=c[this.cursor++],a===\".\"&&!d&&(d=!0,a=c[this.cursor++]);while(a>=\"0\"&&a<=\"9\");this.cursor--;var e=this.lexExponent();d=d||e;var f=c.substring(b.start,this.cursor);b.value=d?parseFloat(f):parseInt(f)},lexDot:function(a){var b=this.token,c=this.source,d=c[this.cursor];if(d>=\"0\"&&d<=\"9\"){do a=c[this.cursor++];while(a>=\"0\"&&a<=\"9\");this.cursor--,this.lexExponent(),b.type=NUMBER,b.value=parseFloat(b.start,this.cursor)}else b.type=DOT,b.assignOp=null,b.value=\".\"},lexString:function(ch){var token=this.token,input=this.source;token.type=STRING;var hasEscapes=!1,delim=ch;while((ch=input[this.cursor++])!==delim){if(this.cursor==input.length)throw this.newSyntaxError(\"Unterminated string literal\");if(ch===\"\\\\\"){hasEscapes=!0;if(++this.cursor==input.length)throw this.newSyntaxError(\"Unterminated string literal\")}}token.value=hasEscapes?eval(input.substring(token.start,this.cursor)):input.substring(token.start+1,this.cursor-1)},lexRegExp:function(ch){var token=this.token,input=this.source;token.type=REGEXP;do{ch=input[this.cursor++];if(ch===\"\\\\\")this.cursor++;else if(ch===\"[\"){do{if(ch===undefined)throw this.newSyntaxError(\"Unterminated character class\");ch===\"\\\\\"&&this.cursor++,ch=input[this.cursor++]}while(ch!==\"]\")}else if(ch===undefined)throw this.newSyntaxError(\"Unterminated regex\")}while(ch!==\"/\");do ch=input[this.cursor++];while(ch>=\"a\"&&ch<=\"z\");this.cursor--,token.value=eval(input.substring(token.start,this.cursor))},lexOp:function(a){var b=this.token,c=this.source,d=opTokens[a],e=c[this.cursor];e in d&&(d=d[e],this.cursor++,e=c[this.cursor],e in d&&(d=d[e],this.cursor++,e=c[this.cursor]));var f=d.op;definitions.assignOps[f]&&c[this.cursor]===\"=\"?(this.cursor++,b.type=ASSIGN,b.assignOp=definitions.tokenIds[definitions.opTypeNames[f]],f+=\"=\"):(b.type=definitions.tokenIds[definitions.opTypeNames[f]],b.assignOp=null),b.value=f},lexIdent:function(a){var b=this.token,c=this.source;do a=c[this.cursor++];while(a>=\"a\"&&a<=\"z\"||a>=\"A\"&&a<=\"Z\"||a>=\"0\"&&a<=\"9\"||a===\"$\"||a===\"_\");this.cursor--;var d=c.substring(b.start,this.cursor);b.type=definitions.keywords[d]||IDENTIFIER,b.value=d},get:function(a){var b;while(this.lookahead){--this.lookahead,this.tokenIndex=this.tokenIndex+1&3,b=this.tokens[this.tokenIndex];if(b.type!==NEWLINE||this.scanNewlines)return b.type}this.skip(),this.tokenIndex=this.tokenIndex+1&3,b=this.tokens[this.tokenIndex],b||(this.tokens[this.tokenIndex]=b={});var c=this.source;if(this.cursor===c.length)return b.type=END;b.start=this.cursor,b.lineno=this.lineno;var d=c[this.cursor++];if(d>=\"a\"&&d<=\"z\"||d>=\"A\"&&d<=\"Z\"||d===\"$\"||d===\"_\")this.lexIdent(d);else if(a&&d===\"/\")this.lexRegExp(d);else if(d in opTokens)this.lexOp(d);else if(d===\".\")this.lexDot(d);else if(d>=\"1\"&&d<=\"9\")this.lexNumber(d);else if(d===\"0\")this.lexZeroNumber(d);else if(d==='\"'||d===\"'\")this.lexString(d);else if(this.scanNewlines&&d===\"\\n\")b.type=NEWLINE,b.value=\"\\n\",this.lineno++;else throw this.newSyntaxError(\"Illegal token\");b.end=this.cursor;return b.type},unget:function(){if(++this.lookahead===4)throw\"PANIC: too much lookahead!\";this.tokenIndex=this.tokenIndex-1&3},newSyntaxError:function(a){var b=new SyntaxError(a,this.filename,this.lineno);b.source=this.source,b.lineno=this.lineno,b.cursor=this.lookahead?this.tokens[this.tokenIndex+this.lookahead&3].start:this.cursor;return b}},exports.Tokenizer=Tokenizer}),define(\"ace/narcissus/jsdefs\",function(a,b,c){function y(a){this.elts=a||null}function x(){this.table=Object.create(null,{}),this.size=0}function v(){return undefined}function u(a){return{getOwnPropertyDescriptor:function(b){var c=Object.getOwnPropertyDescriptor(a,b);c.configurable=!0;return c},getPropertyDescriptor:function(b){var c=s(a,b);c.configurable=!0;return c},getOwnPropertyNames:function(){return Object.getOwnPropertyNames(a)},defineProperty:function(b,c){Object.defineProperty(a,b,c)},\"delete\":function(b){return delete a[b]},fix:function(){if(Object.isFrozen(a))return t(a);return undefined},has:function(b){return b in a},hasOwn:function(b){return({}).hasOwnProperty.call(a,b)},get:function(b,c){return a[c]},set:function(b,c,d){a[c]=d;return!0},enumerate:function(){var b=[];for(m in a)b.push(m);return b},keys:function(){return Object.keys(a)}}}function t(a){var b={};for(var c in Object.getOwnPropertyNames(a))b[c]=Object.getOwnPropertyDescriptor(a,c);return b}function s(a,b){while(a){if(({}).hasOwnProperty.call(a,b))return Object.getOwnPropertyDescriptor(a,b);a=Object.getPrototypeOf(a)}}function r(a){return typeof a===\"function\"&&a.toString().match(/\\[native code\\]/)}function q(a,b,c,d,e,f){Object.defineProperty(a,b,{value:c,writable:!e,configurable:!d,enumerable:!f})}function p(a,b,c,d,e){Object.defineProperty(a,b,{get:c,configurable:!d,enumerable:!e})}b.options={version:185},function(){b.hostGlobal=this}();var d=[\"END\",\"\\n\",\";\",\",\",\"=\",\"?\",\":\",\"CONDITIONAL\",\"||\",\"&&\",\"|\",\"^\",\"&\",\"==\",\"!=\",\"===\",\"!==\",\"<\",\"<=\",\">=\",\">\",\"<<\",\">>\",\">>>\",\"+\",\"-\",\"*\",\"/\",\"%\",\"!\",\"~\",\"UNARY_PLUS\",\"UNARY_MINUS\",\"++\",\"--\",\".\",\"[\",\"]\",\"{\",\"}\",\"(\",\")\",\"SCRIPT\",\"BLOCK\",\"LABEL\",\"FOR_IN\",\"CALL\",\"NEW_WITH_ARGS\",\"INDEX\",\"ARRAY_INIT\",\"OBJECT_INIT\",\"PROPERTY_INIT\",\"GETTER\",\"SETTER\",\"GROUP\",\"LIST\",\"LET_BLOCK\",\"ARRAY_COMP\",\"GENERATOR\",\"COMP_TAIL\",\"IDENTIFIER\",\"NUMBER\",\"STRING\",\"REGEXP\",\"break\",\"case\",\"catch\",\"const\",\"continue\",\"debugger\",\"default\",\"delete\",\"do\",\"else\",\"false\",\"finally\",\"for\",\"function\",\"if\",\"in\",\"instanceof\",\"let\",\"new\",\"null\",\"return\",\"switch\",\"this\",\"throw\",\"true\",\"try\",\"typeof\",\"var\",\"void\",\"yield\",\"while\",\"with\"],e=[\"break\",\"const\",\"continue\",\"debugger\",\"do\",\"for\",\"if\",\"return\",\"switch\",\"throw\",\"try\",\"var\",\"yield\",\"while\",\"with\"],f={\"\\n\":\"NEWLINE\",\";\":\"SEMICOLON\",\",\":\"COMMA\",\"?\":\"HOOK\",\":\":\"COLON\",\"||\":\"OR\",\"&&\":\"AND\",\"|\":\"BITWISE_OR\",\"^\":\"BITWISE_XOR\",\"&\":\"BITWISE_AND\",\"===\":\"STRICT_EQ\",\"==\":\"EQ\",\"=\":\"ASSIGN\",\"!==\":\"STRICT_NE\",\"!=\":\"NE\",\"<<\":\"LSH\",\"<=\":\"LE\",\"<\":\"LT\",\">>>\":\"URSH\",\">>\":\"RSH\",\">=\":\"GE\",\">\":\"GT\",\"++\":\"INCREMENT\",\"--\":\"DECREMENT\",\"+\":\"PLUS\",\"-\":\"MINUS\",\"*\":\"MUL\",\"/\":\"DIV\",\"%\":\"MOD\",\"!\":\"NOT\",\"~\":\"BITWISE_NOT\",\".\":\"DOT\",\"[\":\"LEFT_BRACKET\",\"]\":\"RIGHT_BRACKET\",\"{\":\"LEFT_CURLY\",\"}\":\"RIGHT_CURLY\",\"(\":\"LEFT_PAREN\",\")\":\"RIGHT_PAREN\"},g={\"__proto__\":null},h={},i=\"const \";for(var j=0,k=d.length;j<k;j++){j>0&&(i+=\", \");var l=d[j],m;/^[a-z]/.test(l)?(m=l.toUpperCase(),g[l]=j):m=/^\\W/.test(l)?f[l]:l,i+=m+\" = \"+j,h[m]=j,d[l]=j}i+=\";\";var n={\"__proto__\":null};for(j=0,k=e.length;j<k;j++)n[g[e[j]]]=!0;var o=[\"|\",\"^\",\"&\",\"<<\",\">>\",\">>>\",\"+\",\"-\",\"*\",\"/\",\"%\"];for(j=0,k=o.length;j<k;j++)l=o[j],o[l]=d[l];var w=({}).hasOwnProperty;x.prototype={has:function(a){return w.call(this.table,a)},set:function(a,b){w.call(this.table,a)||this.size++,this.table[a]=b},get:function(a){return this.table[a]},getDef:function(a,b){w.call(this.table,a)||(this.size++,this.table[a]=b());return this.table[a]},forEach:function(a){var b=this.table;for(var c in b)a.call(this,c,b[c])},toString:function(){return\"[object StringMap]\"}},y.prototype={push:function(a){return new y({top:a,rest:this.elts})},top:function(){if(!this.elts)throw new Error(\"empty stack\");return this.elts.top},isEmpty:function(){return this.top===null},find:function(a){for(var b=this.elts;b;b=b.rest)if(a(b.top))return b.top;return null},has:function(a){return Boolean(this.find(function(b){return b===a}))},forEach:function(a){for(var b=this.elts;b;b=b.rest)a(b.top)}},b.tokens=d,b.opTypeNames=f,b.keywords=g,b.isStatementStartCode=n,b.tokenIds=h,b.consts=i,b.assignOps=o,b.defineGetter=p,b.defineProperty=q,b.isNativeCode=r,b.makePassthruHandler=u,b.noPropFound=v,b.StringMap=x,b.Stack=y})"
  },
  {
    "path": "dirigible/shared/static/dirigible/examples/pricelist-json-api-demo.html",
    "content": "<html>\r\n    <head>\r\n        <title>Dirigible pricelist</title>\r\n        <script\r\n            type=\"text/javascript\"\r\n            src=\"/static/jquery/jquery-1.4.2.min.js\"\r\n        ></script>\r\n        <script\r\n            type=\"text/javascript\"\r\n            src=\"/static/json/json2.js\"\r\n        ></script>\r\n        <script type=\"text/javascript\">\r\n\r\nvar URL = \"/user/admin/sheet/126/v0.1/json/\";\r\n\r\nvar COL_STOCK_NAME = 1;\r\n\r\nvar COL_ITEM_NAME = 5;\r\nvar COL_ITEM_NAME_LETTER  = 'E';\r\nvar COL_ITEM_QUANTITY = 6;\r\nvar COL_ITEM_QUANTITY_LETTER = 'F';\r\nvar COL_ITEM_EACH = 7;\r\n\r\nvar request = null;\r\n\r\n\r\nfunction get_overrides() {\r\n    var overrides = {};\r\n    var item;\r\n    var id = 1;\r\n    var override_row = 2;\r\n    // iterate through rows in the html table\r\n    while (true) {\r\n        item = $('#id_item_' + id);\r\n        if (item.length == 0) {\r\n            break;\r\n        }\r\n\r\n        name = item.text();\r\n        value = $('#id_amount_' + id)[0].value;\r\n\r\n        if (value) {\r\n            overrides[COL_ITEM_NAME_LETTER + override_row] = name;\r\n            overrides[COL_ITEM_QUANTITY_LETTER + override_row] = value;\r\n            override_row += 1;\r\n        }\r\n\r\n        id += 1;\r\n    }\r\n    return overrides;\r\n}\r\n\r\nfunction get_items(sheet) {\r\n    var items = {};\r\n    var item;\r\n    var row = 2;\r\n\r\n    // iterate through items listed in the json spreadsheet\r\n    while (true) {\r\n        item = get_item(sheet, row);\r\n        if (item === undefined) {\r\n            break;\r\n        }\r\n        items[item.name] = item;\r\n        row += 1;\r\n    }\r\n    return items;\r\n}\r\n\r\nfunction get_item(sheet, n) {\r\n    var item;\r\n    if (sheet[COL_ITEM_QUANTITY][n] != undefined) {\r\n        item = {\r\n            name: sheet[COL_ITEM_NAME][n],\r\n            quantity: sheet[COL_ITEM_QUANTITY][n],\r\n            each: sheet[COL_ITEM_EACH][n]\r\n        };\r\n    }\r\n    return item;\r\n}\r\n\r\nfunction display_item(id, item) {\r\n    var each = \"<nobr />\";\r\n    var subtotal = \"<nobr />\";\r\n    if (item !== undefined && item.each) {\r\n        each = item.each;\r\n        subtotal = item.quantity * item.each;\r\n        item.subtotal = subtotal;\r\n    }\r\n    $(\"#id_each_\" + id).html(each);\r\n    $(\"#id_subtotal_\" + id).html(subtotal);\r\n}\r\n\r\nfunction display_results(items) {\r\n    var id = 1;\r\n    var item;\r\n    var total = 0;\r\n    var name;\r\n\r\n    // iterate through rows in the html table\r\n    while (true) {\r\n        item = $('#id_item_' + id);\r\n        if (item.length == 0) {\r\n            break;\r\n        }\r\n\r\n        name = $('#id_item_' + id).text();\r\n        item = items[name];\r\n        display_item(id, item);\r\n        if (item && item.subtotal) {\r\n            total += item.subtotal\r\n        }\r\n\r\n        id += 1;\r\n    }\r\n    $(\"#id_total\").html(total);\r\n}\r\n\r\nfunction handle_response(sheet) {\r\n    var items;\r\n    $(\"#id_status\").html(\"recieved response...\");\r\n    if (sheet.usercode_error) {\r\n        $(\"#id_status\").html(\r\n            sheet.usercode_error.message +\r\n            ', line ' + sheet.usercode_error.line\r\n        );\r\n    } else {\r\n        items = get_items(sheet);\r\n        display_results(items);\r\n        $(\"#id_status\").html(\"done.\");\r\n    }\r\n}\r\n\r\nfunction handle_click(event) {\r\n    json_request(URL, get_overrides(), handle_response);\r\n\r\n    $(\"#id_status\").html(\"sent request...\");\r\n\r\n    event.preventDefault(); // disable normal form submit behavior\r\n    return false; // prevent further bubbling of event\r\n}\r\n\r\n\r\nfunction json_request(json_url, overrides, handle_response) {\r\n    if (window.XDomainRequest) {\r\n        // For IE\r\n        xdr = new XDomainRequest();\r\n        if (xdr) {\r\n            xdr.onload = function() {\r\n                handle_response( $.parseJSON( xdr.responseText ) );\r\n            };\r\n            xdr.open('get', json_url + '?' + $.param(overrides));\r\n            xdr.send();\r\n        }\r\n    } else {\r\n        // For everyone else\r\n        $.getJSON(json_url, overrides, handle_response);\r\n    }\r\n\r\n}\r\n\r\nfunction get_stocks(sheet) {\r\n    var stocks = {};\r\n    var stock;\r\n    var row = 2;\r\n\r\n    // iterate through stocks listed in the json spreadsheet\r\n    while (true) {\r\n        stock = sheet[COL_STOCK_NAME][row];\r\n        if (stock === undefined) {\r\n            break;\r\n        }\r\n        stocks[stock] = 1;\r\n        row += 1;\r\n    }\r\n    return stocks;\r\n}\r\n\r\nfunction populate_table(sheet) {\r\n    var stocks = get_stocks(sheet);\r\n    var header = $('#id_header');\r\n    var row_html;\r\n    var id = 1;\r\n    $(\"#id_status\").html(\"stocks loaded...\");\r\n    for (stock in stocks) {\r\n        row_html = '\\\r\n            <tr> \\\r\n                <td id=\"id_item_' + id + '\">' + stock + '</td> \\\r\n                <td><input id=\"id_amount_' + id + '\" /></td> \\\r\n                <td id=\"id_each_' + id + '\"><nobr /></td> \\\r\n                <td id=\"id_subtotal_' + id + '\"><nobr /></td> \\\r\n            </tr>';\r\n        header.after(row_html);\r\n        id += 1;\r\n    }\r\n    $(\"#id_status\").html(\"ready\");\r\n}\r\n\r\nfunction init() {\r\n    $(\"#id_status\").html(\"requesting available stocks...\");\r\n    json_request(URL, {}, populate_table);\r\n}\r\n\r\n$( init );\r\n\r\n        </script>\r\n        <style>\r\nbody {\r\n    background-color: #DDD;\r\n}\r\n        </style>\r\n    </head>\r\n    <body>\r\n        <table border=1 cellpadding=0 cellspacing=0>\r\n            <tr id=\"id_header\">\r\n                <th>Item</th>\r\n                <th>Amount</th>\r\n                <th>Each</th>\r\n                <th>Subtotal</th>\r\n            </tr>\r\n            <tr>\r\n                <td colspan=\"3\" align=\"right\">Total:</td>\r\n                <td id=\"id_total\"><nobr /></td>\r\n            </tr>\r\n        </table>\r\n\r\n        <input\r\n            name=\"Submit\"\r\n            type=\"button\"\r\n            value=\"Update\"\r\n            onclick=\"return handle_click(event);\"\r\n        />\r\n\r\n        <div id=\"id_status\"><nobr /></div>\r\n\r\n    </body>\r\n</html>\r\n\r\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/scripts/cell_editor.js",
    "content": "// Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\r\n// See LICENSE.md\r\n//\r\n\r\n\r\neditor_methods = {\r\n\r\n    init: function(args) {\r\n        this.$input = $(\"<input type='text' class='editor-text' />\")\r\n            .bind(\"keydown.nav\", function(e) {\r\n                if (e.keyCode === $.ui.keyCode.LEFT || e.keyCode === $.ui.keyCode.RIGHT) {\r\n                    e.stopImmediatePropagation();\r\n                }\r\n            })\r\n            .appendTo(args.container);\r\n        if (document.activeElement !== args.formula_bar[0] ) {\r\n            this.$input.focus();\r\n        }\r\n        this.column = args.column;\r\n        this.$formula_bar = args.formula_bar;\r\n\r\n        var that = this;\r\n        var updating = false;\r\n\r\n        this.updateFormulaBarIfChanged = function(new_value) {\r\n            if (that.$formula_bar.val() !== new_value) {\r\n                that.$formula_bar.val(new_value);\r\n            }\r\n        };\r\n        this._updateFormulaBar = function() {\r\n            if (updating) {\r\n                return;\r\n            }\r\n            updating = true;\r\n            that.updateFormulaBarIfChanged(that.$input.val());\r\n            updating = false;\r\n        };\r\n        this.$input.bind(\"input\", this._updateFormulaBar);\r\n        this.$input.bind(\"propertychange\", this._updateFormulaBar);\r\n\r\n        this.updateInput = function() {\r\n            if (updating) {\r\n                return;\r\n            }\r\n            updating = true;\r\n            that.$input.val(that.$formula_bar.val());\r\n            updating = false;\r\n        };\r\n        this.$formula_bar.bind(\"input\", this.updateInput);\r\n        this.$formula_bar.bind(\"propertychange\", this.updateInput);\r\n\r\n        this.forwardReturnsToEditor = function(evt) {\r\n            if (evt.which == 13) {\r\n                var refirableEvent = $.Event(\"keydown\");\r\n                refirableEvent.which = 13;\r\n                that.$input.trigger(refirableEvent);\r\n            }\r\n        };\r\n        this.$formula_bar.bind(\"keydown\", this.forwardReturnsToEditor);\r\n    },\r\n\r\n    destroy: function () {\r\n        this.$input.remove();\r\n        this.$formula_bar.unbind(\"input\", this.updateInput);\r\n        this.$formula_bar.unbind(\"propertychange\", this.updateInput);\r\n        this.$formula_bar.unbind(\"keydown\", this.forwardReturnsToEditor);\r\n    },\r\n\r\n    loadValue: function (gridRow) {\r\n        var newValue = \"\";\r\n        if (gridRow !== undefined && gridRow[this.column.field] !== undefined) {\r\n            newValue = gridRow[this.column.field].formula || \"\";\r\n        }\r\n        this.defaultValue = newValue;\r\n        this.setInputElementValues(newValue);\r\n        this.$input[0].defaultValue = newValue;\r\n    },\r\n\r\n    setInputElementValues: function(value) {\r\n        this.$input.val(value);\r\n        this.$input.caret(value.length, value.length);\r\n        this.updateFormulaBarIfChanged(value);\r\n    },\r\n\r\n    serializeValue: function () {\r\n        return {formula: this.$input.val()};\r\n    },\r\n\r\n    applyValue: function (item, state) {\r\n        item[this.column.field] = state;\r\n    },\r\n\r\n    isValueChanged: function () {\r\n        return (\r\n            !(this.$input.val() === \"\" && this.defaultValue === null) &&\r\n            this.$input.val() !== this.defaultValue\r\n        );\r\n    },\r\n\r\n    validate: function () {\r\n        return {\r\n            valid: true,\r\n            msg: null\r\n        };\r\n    }\r\n};\r\n\r\n\r\nfunction CellEditorFactory(currying_formula_bar) {\r\n    return function(args) {\r\n        $.extend(this, editor_methods);\r\n        this.init($.extend({ formula_bar : currying_formula_bar }, args));\r\n    };\r\n}\r\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/scripts/console_view.js",
    "content": "// Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n// See LICENSE.md\n//\n\n(function($) {\n    $.extend(true, window, {\n        \"Dirigible\": {\n            \"ConsoleView\": ConsoleView\n        }\n    });\n\n    function ConsoleView($consoleDiv) {\n        var self = this;\n\n        self.updateMetaData = function(sheetMetaData) {\n            if ('console_text' in sheetMetaData) {\n                $consoleDiv.html(sheetMetaData['console_text']);\n            } else {\n                $consoleDiv.html('');\n            }\n        };\n    }\n\n})(jQuery);\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/scripts/dialogs.js",
    "content": "(function($) {\n    $.extend(true, window, {\n        \"Dirigible\": {\n            \"Dialogs\": {\n                \"Initialise\": Initialise\n            }\n        }\n    });\n\nfunction Initialise(grid, urls) {\n    var _self = this;\n    _self.grid = grid;\n\n    $('#id_import_form_file').change(function() {\n        if($('#id_import_form_file').val().match(/\\.xls$/)) {\n            $('#id_import_form_upload_xls_values_button').show();\n            $('#id_import_form_upload_csv_button').hide();\n            $('#id_import_form span.import_form_radio_button').hide();\n            $('#id_import_form form').attr('action', urls.importXls);\n        } else {\n            $('#id_import_form span.import_form_radio_button').show();\n            $('#id_import_form_upload_csv_button').show();\n            $('#id_import_form_upload_xls_values_button').hide();\n            $('#id_import_form form').attr('action', urls.importCsv);\n        }\n    });\n\n    $('#id_import_form_cancel_button').click( function() {\n        $('#id_import_form').dialog(\"close\");\n    });\n    $('#id_export_dialog_close_button').click( function() {\n        $('#id_export_dialog').dialog(\"close\");\n    });\n\n    $('#id_import_button').click(importButtonClickHandler);\n    $('#id_export_button').click(function() {$('#id_export_dialog').dialog({width: 450});});\n\n    function importButtonClickHandler() {\n        $('#id_import_form form')[0].reset();\n        var column = 1;\n        var row = 1;\n        var gridActiveCell = _self.grid.getActiveCell();\n        if (gridActiveCell) {\n            column = gridActiveCell.cell;\n            row = gridActiveCell.row + 1;\n            $('#id_import_form_column').val(column);\n            $('#id_import_form_row').val(row);\n        }\n        $('#id_import_form_upload_csv_button').hide();\n        $('#id_import_form span.import_form_radio_button').hide();\n        $('#id_import_form_upload_xls_values_button').hide();\n        $('#id_import_form').dialog();\n    }\n}\n\n})(jQuery);\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/scripts/editor_commands.js",
    "content": "// Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n// See LICENSE.md\n//\n\n\n(function($) {\n    // register namespace\n    $.extend(true, window, {\n        \"Dirigible\": {\n            \"EditorCommands\": EditorCommands\n        }\n    });\n\n    function EditorCommands(urls, editor) {\n\n        function saveUsercode() {\n            if (!editor.usercodeDirty) {\n                return;\n            }\n            Dirigible.SheetUtils.abortOtherRecalculations();\n            var text = editor.getSession().getValue();\n            $.post(\n                urls.setSheetUsercode,\n                { usercode: text },\n                Dirigible.SheetUtils.queueRecalculation\n            );\n            editor.usercodeDirty = false;\n        }\n\n        $.extend(\n            this,\n            {\n                'saveUsercode': saveUsercode\n            }\n        );\n    }\n\n})(jQuery);\n\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/scripts/feedback_dialog.js",
    "content": "(function($) {\n    $.extend(true, window, {\n        \"Dirigible\": {\n            \"FeedbackDialog\": {\n                \"Initialise\": Initialise\n            }\n        }\n    });\n\nfunction Initialise(urls, username) {\n    var _self = this;\n\n    var default_email = \"Email address (optional - only necessary if you would like us to contact you)\";\n    if (username !== '') {\n        $('#id_feedback_dialog_email_address').hide();\n    } else {\n        $('#id_feedback_dialog_email_address').val(default_email);\n        $('#id_feedback_dialog_email_address').addClass('email_prompt');\n        $('#id_feedback_dialog_email_address').focus(function() {\n            $('#id_feedback_dialog_email_address').removeClass('email_prompt');\n            if ($('#id_feedback_dialog_email_address').val() == default_email) {\n                $('#id_feedback_dialog_email_address').val('');\n            }\n        });\n    }\n\n    $('.feedback_link').click(function(event) {\n        $('#id_feedback_dialog_error').addClass('hidden');\n        $('#id_feedback_dialog_spinner').addClass('hidden');\n        $('#id_feedback_dialog_ok_button').removeAttr('disabled');\n        $('#id_feedback_dialog_cancel_button').removeAttr('disabled');\n        $('#id_feedback_dialog').dialog({\n                width: 600\n        });\n        event.preventDefault();\n    });\n\n    $('#id_feedback_dialog_ok_button').click(function() {\n        $('#id_feedback_dialog_spinner').removeClass('hidden');\n        $('#id_feedback_dialog_ok_button').attr('disabled', 'disabled');\n        $('#id_feedback_dialog_cancel_button').attr('disabled', 'disabled');\n        $('#id_feedback_dialog_error').addClass('hidden');\n        $.ajax({\n            type:'POST',\n            url: urls.feedback,\n            data: {\n                message: $('#id_feedback_dialog_text').val(),\n                email_address: $('#id_feedback_dialog_email_address').val(),\n                username: username\n            },\n            error: function() {\n                $('#id_feedback_dialog_error').removeClass('hidden');\n                $('#id_feedback_dialog_ok_button').removeAttr('disabled');\n                $('#id_feedback_dialog_cancel_button').removeAttr('disabled');\n                $('#id_feedback_dialog_spinner').addClass('hidden');\n            },\n            success: function() {\n                $('#id_feedback_dialog').dialog(\"close\");\n            }\n        });\n    });\n\n    $('#id_feedback_dialog_cancel_button').click(function() {\n        $('#id_feedback_dialog_error').addClass('hidden');\n        $('#id_feedback_dialog').dialog(\"close\");\n    });\n\n}\n\n})(jQuery);\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/scripts/grid_commands.js",
    "content": "// Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n// See LICENSE.md\n//\n\n\n(function($) {\n    // register namespace\n    $.extend(true, window, {\n        \"Dirigible\": {\n            \"GridCommands\": GridCommands\n        }\n    });\n\n\n    function GridCommands(urls, grid) {\n        var self = this;\n        self.urls = urls;\n\n        self.getPostFunction__ = function (name, recalculate) {\n            var callback = undefined;\n            if (recalculate) {\n                callback = Dirigible.SheetUtils.queueRecalculation;\n            }\n\n            return function (startCol, startRow, endCol, endRow) {\n                $.post(\n                    self.urls[name],\n                    {range: startCol+','+startRow+','+endCol+','+endRow},\n                    callback\n                );\n            };\n        };\n\n\n        self.sendCopyToServer__ = self.getPostFunction__('copy', false);\n        self.sendCutToServer__ = self.getPostFunction__('cut', true);\n        self.sendPasteToServer__ =self.getPostFunction__('paste', true);\n        self.sendClearToServer__ = self.getPostFunction__('clearCells', true);\n\n        self.copy = Dirigible.GridCommands.createSelectionCommand__(\n                    grid, self.sendCopyToServer__);\n        self.cut = Dirigible.GridCommands.createSelectionCommand__(\n                    grid, self.sendCutToServer__);\n        self.paste = Dirigible.GridCommands.createSelectionCommand__(\n                    grid, self.sendPasteToServer__);\n        self.clear = Dirigible.GridCommands.createSelectionCommand__(\n                    grid, self.sendClearToServer__);\n\n\n        self.resizeColumns = function(gridColumns) {\n            var columnWidths = self.convertGridColumnsToJSON__(grid.getColumns());\n            $.post(urls.setColumnWidths, {column_widths: columnWidths});\n        };\n\n\n        self.convertGridColumnsToJSON__ = function(gridColumns) {\n            var columnWidths = {};\n            for (var i=0; i < gridColumns.length; i++) {\n                var col = gridColumns[i];\n                if (col.previousWidth != col.width) {\n                    columnWidths[String(i)] = col.width;\n                }\n            }\n            return JSON.stringify(columnWidths);\n        };\n\n\n        self.sendCellChangeToServer = function(event, gridLocation, retries) {\n            Dirigible.SheetUtils.abortOtherRecalculations();\n            retries = (retries === undefined) ? 5 : retries;\n            $.post(\n                urls.setCellFormula,\n                slickGridCellDataToPostParams(grid, gridLocation),\n                function(response) {\n                    if (response === 'FAILED' && retries > 0) {\n                        self.sendCellChangeToServer(\n                            event, gridLocation, retries - 1\n                        );\n                    } else {\n                        self.setCellFormulaAndUpdateSheet__(gridLocation);\n                    }\n                }\n            );\n        };\n\n\n        self.setCellFormulaAndUpdateSheet__ = function (gridLocation) {\n            grid.updateCell(gridLocation.row, gridLocation.column);\n            Dirigible.SheetUtils.queueRecalculation();\n        };\n\n        self.commitCurrentEdit = function () {\n            grid.getEditController().commitCurrentEdit();\n        }\n\n    }\n\n    Dirigible.GridCommands.createSelectionCommand__ = function(grid, command) {\n        return function() {\n            if (grid.getCellEditor() === null) {\n                var selectedRange = (\n                    grid.getSelectionModel().getSelectedRanges()[0]\n                );\n                command(\n                    selectedRange.fromCell,\n                    selectedRange.fromRow + 1,\n                    selectedRange.toCell,\n                    selectedRange.toRow + 1\n                );\n                return true;\n            }\n            return false;\n        };\n    };\n\n})(jQuery);\n\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/scripts/grid_content_converter.js",
    "content": "// Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n// See LICENSE.md\n//\n\nfunction getGridCellFormula(grid, slickCellLocation) {\n    var columnField = grid.getColumns()[slickCellLocation.cell].field;\n    var rowData = grid.getDataItem(slickCellLocation.row);\n    if (rowData !== undefined) {\n        var cellData = rowData[columnField];\n        if (cellData !== undefined && cellData.formula !== undefined) {\n            return cellData.formula;\n        }\n    }\n    return \"\";\n}\n\n\nfunction slickGridCellDataToPostParams(grid, slickCellLocation) {\n    return {\n        column: slickCellLocation.cell,\n        row: slickCellLocation.row + 1,\n        formula: grid.getCellFormula(slickCellLocation)\n    };\n}\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/scripts/grid_interaction_handler.js",
    "content": "(function($) {\n    // register namespace\n    $.extend(true, window, {\n        \"Dirigible\": {\n            \"GridInteractionHandler\": GridInteractionHandler\n        }\n    });\n\n    function GridInteractionHandler($, grid, gridCommands, gridRemoteModel) {\n        var self = this;\n        self.handleKeyDown = function(event) {\n            var TAB       = 9;\n            var DEL       = 46;\n            var BACKSPACE = 8;\n            var LEFT      = 37;\n            var UP        = 38;\n            var PAGE_DOWN = 34;\n            var PAGE_UP   = 33;\n            var HOME      = 36;\n            var END       = 35;\n            var LETTER_C  = 67;\n            var LETTER_V  = 86;\n            var LETTER_X  = 88;\n            var F2        = 113;\n\n            if (event.ctrlKey || event.metaKey) {\n                switch (event.which) {\n                    case LETTER_X:\n                        if (gridCommands.cut()) {\n                            event.stopImmediatePropagation();\n                        }\n                        break;\n                    case LETTER_C:\n                        if (gridCommands.copy()) {\n                            event.stopImmediatePropagation();\n                        }\n                        break;\n                    case LETTER_V:\n                        if (gridCommands.paste()) {\n                            event.stopImmediatePropagation();\n                        }\n                        break;\n                    case HOME:\n                        grid.gotoCell(0, 1);\n                        event.stopImmediatePropagation();\n                        break;\n                    case END:\n                        grid.gotoCell(grid.getDataLength() - 1, grid.getColumns().length - 1);\n                        event.stopImmediatePropagation();\n                        break;\n                }\n            } else {\n                switch (event.which) {\n                    case BACKSPACE:\n                    case DEL:\n                        if (gridCommands.clear()) {\n                            event.stopImmediatePropagation();\n                        }\n                        break;\n                    case LEFT:\n                        if (grid.getActiveCell().cell === 1) {\n                            event.stopImmediatePropagation();\n                        }\n                        break;\n\n                    case UP:\n                        if (grid.getActiveCell().row === 0) {\n                            event.stopImmediatePropagation();\n                        }\n                        break;\n\n                    case PAGE_DOWN:\n                        var viewport = grid.getViewport();\n                        var newTopRow = viewport.bottom - 1;\n                        var activeCell = grid.getActiveCell();\n                        var pageSize = viewport.bottom - viewport.top - 1;\n                        if (viewport.bottom < grid.getDataLength()) {\n                            grid.scrollRowIntoView(newTopRow, true);\n                        }\n                        grid.gotoCell(activeCell.row + pageSize, activeCell.cell);\n                        event.stopImmediatePropagation();\n                        break;\n\n                    case PAGE_UP:\n                        var viewport = grid.getViewport();\n                        var activeCell = grid.getActiveCell();\n                        var pageSize = viewport.bottom - viewport.top - 1;\n                        var newTopRow = viewport.top - pageSize;\n                        if (newTopRow < 0) {\n                            newTopRow = 0;\n                        }\n                        grid.scrollRowIntoView(newTopRow, false);\n                        grid.gotoCell(activeCell.row - pageSize, activeCell.cell);\n                        event.stopImmediatePropagation();\n                        break;\n\n                    case F2:\n                        grid.editActiveCell();\n                        break;\n                }\n            }\n        };\n\n        self.handleGridDivKeyPress = function(event) {\n            if (event.ctrlKey || event.altKey) {\n                return true;\n            }\n            if (event.which > 47 || event.which === 32) {\n                if (grid.getCellEditor() === null ) {\n                    grid.editActiveCell();\n                    grid.getCellEditor().setInputElementValues(String.fromCharCode(event.which));\n                    return false;\n                }\n            }\n            return true;\n        };\n\n        self.updateFormulaBar = function() {\n            var activeCell = grid.getActiveCell();\n            if (!activeCell) {\n                return;\n            }\n            $('#id_formula_bar').val(grid.getCellFormula(activeCell));\n        };\n\n        self.handleFormulaBarClick = function () {\n            if (grid.getCellEditor() === null) {\n                grid.editActiveCell();\n            }\n            var $formulaBar = $('#id_formula_bar');\n            var caret = $formulaBar.caret();\n            $formulaBar.focus();\n            $formulaBar.caret(caret.start, caret.end);\n            \n        };\n\n        self.bind = function() {\n            grid.onKeyDown.subscribe(self.handleKeyDown);\n\n            $('#id_grid').keypress(self.handleGridDivKeyPress);\n            $('#id_formula_bar').click(self.handleFormulaBarClick);\n\n            grid.onColumnsResized.subscribe(gridCommands.resizeColumns);\n            grid.onCellChange.subscribe(gridCommands.sendCellChangeToServer);\n            grid.onActiveCellChanged.subscribe(self.updateFormulaBar);\n            grid.onClick.subscribe(self.updateFormulaBar);\n        };\n    }\n\n})(jQuery);\n\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/scripts/grid_remote_model.js",
    "content": "(function($) {\n    $.extend(true, window, {\n        \"Dirigible\": {\n            \"GridRemoteModel\": GridRemoteModel\n        }\n    });\n\n\tfunction GridRemoteModel(urls) {\n        var self = this;\n\n        self.data = [];\n        self.url = urls.getJSONGridData;\n        self.patches = {};\n        self.pending = {};\n        self.patchWidth = 26;\n        self.patchHeight = 100;\n        \n        self.getLength = function() {\n            return self.data.length;\n        };\n        \n        self.getItem = function(index) {\n            return self.data[index];\n        };\n\n        self.cellToPatch = function(col, row) {\n            return {\n                patchCol: Math.floor( Math.max((col - 1), 0) / self.patchWidth ),\n                patchRow: Math.floor( Math.max((row - 1), 0) / self.patchHeight )\n            };\n        };\n\n        self.patchToCells = function(patch) {\n            return {\n                left: patch.patchCol * self.patchWidth + 1,\n                topmost: patch.patchRow * self.patchHeight + 1,\n                right: patch.patchCol * self.patchWidth + self.patchWidth,\n                bottom: patch.patchRow * self.patchHeight + self.patchHeight\n            };\n        };\n\n\t\tself.choosePatchesToLoad = function(left, topmost, right, bottom) {\n            var patchesToLoad = [];\n            var nw = self.cellToPatch(left, topmost);\n            var se = self.cellToPatch(right, bottom);\n            for (var patchCol = nw.patchCol; patchCol <= se.patchCol; patchCol++) {\n                for (var patchRow = nw.patchRow; patchRow <= se.patchRow; patchRow++) {\n                    patchesToLoad.push( { patchCol: patchCol, patchRow: patchRow } );\n                }\n            }\n            return patchesToLoad;\n        };\n\n\n        self.isPatchLoadedOrPending = function(patch) {\n            if (self.patches[patch.patchCol] !== undefined) {\n                if(self.patches[patch.patchCol][patch.patchRow] !== undefined){\n                    return true;\n                }\n            }\n            if (self.pending[patch.patchCol] !== undefined) {\n                if(self.pending[patch.patchCol][patch.patchRow] !== undefined){\n                    return true;\n                }\n            }\n            return false;\n        };\n\n\n        self.loadPatchIfNecessary = function(patch) {\n            if (!self.isPatchLoadedOrPending(patch)) {\n                var range = self.patchToCells(patch);\n                if (self.pending[patch.patchCol] === undefined){\n                    self.pending[patch.patchCol] = {};\n                }\n                self.pending[patch.patchCol][patch.patchRow] = true;\n                self.onDataLoading.notify();\n                self.getData(range.left, range.topmost, range.right, range.bottom, patch);\n            }\n        };\n\n\n        self.getData = function (left, topmost, right, bottom, patch) {\n            var range = left + ', ' + topmost + ', ' + right + ', ' + bottom;\n            $.getJSON(self.url, {'range' : range}, self.getSuccessHandlerForPatch(patch) );\n        };\n\n\n\t\tself.ensureData = function(left, topmost, right, bottom) {\n            var patchesToLoad = self.choosePatchesToLoad(left, topmost, right, bottom);\n            for (var patch=0; patch<patchesToLoad.length; patch++) {\n                self.loadPatchIfNecessary(patchesToLoad[patch]);\n            }\n\t\t};\n\n\n\t\tself.onDataLoading = new Slick.Event();\n\t\tself.onDataLoaded = new Slick.Event();\n\n\n\t\tself.onError = function(fromPage,toPage) {\n\t\t\tconsole.log(\"error loading pages \" + fromPage + \" to \" + toPage);\n\t\t};\n\n\n        self.addData = function(jsonData) {\n            for (var row=jsonData.topmost; row <= jsonData.bottom; row++ ) {\n                if (self.data[row - 1] === undefined) {\n                    self.data[row - 1] = { header: row };\n                }\n                for (\n                    var column=jsonData.left;\n                    column <= jsonData.right;\n                    column++)\n                {\n                    if (\n                        jsonData[row] !== undefined &&\n                        jsonData[row][column] !== undefined)\n                    {\n                        self.data[row - 1][column] = jsonData[row][column];\n                    } else {\n                        delete self.data[row - 1][column];\n                    }\n                }\n            }\n        };\n\n        self.getSuccessHandlerForPatch = function(patch) {\n            return function(jsonData) {\n                self.onSuccess(jsonData);\n\n                var expectedArea = self.patchToCells(patch);\n                if (jsonData.left > expectedArea.left \n                    || jsonData.topmost > expectedArea.topmost \n                    || jsonData.right < expectedArea.right \n                    || jsonData.bottom < expectedArea.bottom){\n                    return\n                }\n                if (self.patches[patch.patchCol] === undefined){\n                    self.patches[patch.patchCol] = {};\n                }\n                self.patches[patch.patchCol][patch.patchRow] = true;\n                if (self.pending[patch.patchCol] !== undefined) {\n                    delete self.pending[patch.patchCol][patch.patchRow];\n                }\n            };\n        };\n\n\t\tself.onSuccess = function(jsonData) {\n            self.addData(jsonData);\n\t\t\tself.onDataLoaded.notify({\n                    left: jsonData.left,\n                    topmost: jsonData.topmost,\n                    right: jsonData.right,\n                    bottom: jsonData.bottom\n            });\n\t\t};\n\n        self.reset = function(newData) {\n            self.data = newData;\n            self.patches = {};\n            self.pending = {};\n        };\n    }\n})(jQuery);\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/scripts/grid_view.js",
    "content": "// Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n// See LICENSE.md\n//\n\n(function($) {\n    $.extend(true, window, {\n        \"Dirigible\": {\n            \"GridView\": GridView\n        }\n    });\n\n    var editor_locator = 'input.editor-text';\n    var formula_bar_locator = '#id_formula_bar';\n\n    function GridView(grid, remoteModel) {\n        var self = this;\n        self.grid = grid;\n        self.remoteModel = remoteModel;\n\n        self.with_restore_cursor = function( wrapped_function ) {\n            var f = function() {\n                var current_selected_editor_locator = null;\n                var caret = null;\n                if (document.activeElement == $(editor_locator)[0]) {\n                    current_selected_editor_locator = editor_locator;\n                    caret = $(current_selected_editor_locator).caret();\n                } else if (document.activeElement == $(formula_bar_locator)[0]) {\n                    current_selected_editor_locator = formula_bar_locator;\n                    caret = $(current_selected_editor_locator).caret();\n                }\n                var contents = $(editor_locator).val();\n\n                wrapped_function.apply(null, arguments);\n\n                if (current_selected_editor_locator !== null) {\n                    self.grid.editActiveCell();\n                }\n\n                $(editor_locator).val(contents);\n                $(formula_bar_locator).val(contents);\n\n                if (current_selected_editor_locator !== null) {\n                    $(current_selected_editor_locator).focus();\n                    $(current_selected_editor_locator).caret(\n                        caret.start, caret.end\n                    );\n                }\n            };\n\n            f.wrapped = true; // for testing\n            return f\n        };\n\n        self.updateMetaData = self.with_restore_cursor(\n            function(sheetMetaData) {\n                var rows = Dirigible.GridView.jsonToSlickGridRowHeaders(sheetMetaData);\n                self.remoteModel.reset(rows);\n                self.grid.render();\n                self.ensureCurrentViewportData();\n            }\n        );\n\n        self.ensureCurrentViewportData = function() {\n            var $viewport = $('div.slick-viewport');\n            var topLeft = grid.getCellFromPoint(\n                $viewport.scrollLeft(), $viewport.scrollTop()\n            );\n            var bottomRight = grid.getCellFromPoint(\n                $viewport.scrollLeft() + $viewport.width(),\n                $viewport.scrollTop() + $viewport.height()\n            );\n            self.remoteModel.ensureData(\n                topLeft.cell, topLeft.row,\n                bottomRight.cell, bottomRight.row\n            );\n        };\n\n        self.handleOnDataLoading = function() {\n            $('#id_buffering_message').removeClass('hidden');\n        };\n        self.remoteModel.onDataLoading.subscribe(self.handleOnDataLoading);\n\n        self.handleOnDataLoaded = self.with_restore_cursor(\n            function(event, loadedRange) {\n                for (var row=loadedRange.topmost; row <= loadedRange.bottom; row++) {\n                    grid.invalidateRow(row - 1);\n                }\n\n                grid.updateRowCount();\n                grid.render();\n                $('#id_buffering_message').addClass('hidden');\n            }\n        );\n\n        self.remoteModel.onDataLoaded.subscribe(self.handleOnDataLoaded);\n\n        self.grid.onScroll.subscribe(self.ensureCurrentViewportData);\n\n    };\n\n\n    GridView.jsonToSlickGridData = function(jsonData) {\n        var slickgrid_sheet = [];\n        for (i=0; i < jsonData.height; i++) {\n            slickgrid_sheet[i] = jQuery.extend(\n                { header: i + 1 }, jsonData[i + 1]\n            );\n        }\n        return slickgrid_sheet;\n    };\n\n    GridView.jsonToSlickGridRowHeaders = function(jsonData) {\n        var height = jsonData.height || 1000;\n        var slickgrid_sheet = [];\n        for (var ii = 1; ii <= height; ii++) {\n            slickgrid_sheet.push({header: ii});\n        }\n        return slickgrid_sheet;\n    }\n\n    GridView.jsonToSlickGridColumnHeaders = function(jsonData) {\n        var width = jsonData.width || 52;\n        var sheet_columns = [{\n            name: \"\",\n            field: \"header\",\n            id: \"header\",\n            width: 40,\n            sortable: false,\n            unselectable: true\n        }];\n        for (var ii = 1; ii <= width; ii++) {\n            var column_name = GridView.columnIndexToName(ii);\n            var fieldID = \"\" + ii;\n            var column_options = {\n                name: column_name,\n                field: fieldID,\n                id: fieldID,\n                formatter: GridView.cellFormatter};\n            if (jsonData.column_widths && jsonData.column_widths[ii]) {\n                column_options.width = jsonData.column_widths[ii];\n            }\n            sheet_columns.push(column_options);\n        }\n        return sheet_columns;\n    };\n\n    GridView.columnIndexToName = function(index) {\n        var columnName = '';\n        while (index > 0) {\n            var thisChar = String.fromCharCode((index - 1) % 26 + 65);\n            index = Math.floor((index - 1) / 26);\n            columnName = thisChar + columnName;\n        }\n        return columnName;\n    };\n\n\n    GridView.cellFormatter = function(row, column, cell, columnDef, dataContext) {\n        if (cell !== undefined && cell.error !== undefined) {\n            row = row + 1;\n            return \"<img id='id_\" + column + \"_\" + row + \"_error' \" +\n                        \"title='\" + htmlescape(cell.error) + \"' \" +\n                        \"src='/static/dirigible/images/error.gif' />\";\n        }\n        if (cell === undefined) {\n            return \"\";\n        }\n        if (cell.formatted_value === undefined) {\n            if (cell.formula) {\n                var escaped_formula = htmlescape(cell.formula);\n                return (\n                    '<span class=\"grid_formula\" title=\"'+ escaped_formula +'\">' +\n                    escaped_formula + \"</span>\"\n                );\n            } else {\n                return \"\";\n            }\n        }\n        var escaped_formatted_value = htmlescape(cell.formatted_value);\n        return (\n            '<span class=\"grid_formatted_value\" title=\"'+ escaped_formatted_value +'\">' +\n            escaped_formatted_value + \"</span>\"\n        );\n    };\n\n\n})(jQuery);\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/scripts/htmlescape.js",
    "content": "// Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n// See LICENSE.md\n//\n\nfunction htmlescape(untrusted_string) {\n    var better_string = untrusted_string.replace(/&/g, '&amp;');\n    better_string = better_string.replace(/>/g, '&gt;');\n    better_string = better_string.replace(/</g, '&lt;');\n    better_string = better_string.replace(/\"/g, '&quot;');\n    better_string = better_string.replace(/'/g, '&#39;');\n    return better_string;\n}\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/scripts/keyboard_cellrange_selector.js",
    "content": "// Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n// See LICENSE.md\n//\n\nvar TAB = 9;\nvar SHIFT = 16;\n\n\n(function($) {\n    // register namespace\n    $.extend(true, window, {\n        \"Dirigible\": {\n            \"KeyboardCellRangeSelector\": KeyboardCellRangeSelector\n        }\n    });\n\n    function KeyboardCellRangeSelector() {\n        this.isShiftDown = false;\n        this.startCell = null;\n    }\n\n    KeyboardCellRangeSelector.prototype.init = function (grid) {\n        this.grid = grid;\n        grid.onKeyDown.subscribe(this.getKeyDownHandler());\n        grid.onActiveCellChanged.subscribe(this.getActiveCellChangedHandler());\n        $(window.document).keyup(this.getKeyUpHandler());\n    };\n\n    KeyboardCellRangeSelector.prototype.destroy = function () {\n    };\n\n    KeyboardCellRangeSelector.prototype.getKeyDownHandler = function () {\n        var _self = this;\n        return function (event) {\n            if (event.which === SHIFT) {\n                _self.isShiftDown = true;\n                _self.startCell = _self.grid.getActiveCell();\n            }\n            else if (event.which === TAB)\n            {\n                _self.isShiftDown = false;\n                _self.startCell = null;\n            }\n        };\n    };\n    KeyboardCellRangeSelector.prototype.getKeyUpHandler = function () {\n        var _self = this;\n        return function (event) {\n            if (event.which === SHIFT) {\n                _self.isShiftDown = false;\n                _self.startCell = null;\n            }\n        };\n    };\n\n    KeyboardCellRangeSelector.prototype.getActiveCellChangedHandler = function () {\n        var _self = this;\n        return function (event, cell) {\n            if (_self.startCell && _self.isShiftDown) {\n                var new_range = new Slick.Range(\n                        _self.startCell.row, _self.startCell.cell,\n                        cell.row, cell.cell\n                );\n                _self.onCellRangeSelected.notify({range: new_range});\n            }\n        };\n    };\n\n    KeyboardCellRangeSelector.prototype.onBeforeCellRangeSelected = new Slick.Event();\n\n    KeyboardCellRangeSelector.prototype.onCellRangeSelected = new Slick.Event();\n\n\n\n})(jQuery);\n\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/scripts/page_commands.js",
    "content": "// Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n// See LICENSE.md\n//\n\n\n(function($) {\n    // register namespace\n    $.extend(true, window, {\n        \"Dirigible\": {\n            \"PageCommands\": PageCommands\n        }\n    });\n\n    function PageCommands(gridCommands, editorCommands) {\n        var self = this;\n\n        self.recalculate = function() {\n            editorCommands.saveUsercode();\n            gridCommands.commitCurrentEdit();\n            Dirigible.SheetUtils.abortOtherRecalculations();\n            Dirigible.SheetUtils.queueRecalculation();\n        }\n    }\n\n})(jQuery);\n\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/scripts/page_interaction_handler.js",
    "content": "// Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n// See LICENSE.md\n//\n(function($) {\n    // register namespace\n    $.extend(true, window, {\n        \"Dirigible\": {\n            \"PageInteractionHandler\": PageInteractionHandler\n        }\n    });\n\n    function PageInteractionHandler(\n        $, pageView, pageCommands, editor, editorCommands, grid, gridCommands\n    ) {\n        var self = this;\n        self.commitCurrentEdit = function() {\n            gridCommands.commitCurrentEdit();\n        };\n\n        self.initialiseComponents = function(urls, editable) {\n            if (editable) {\n                $('#id_sheet_name').eip(\n                    urls.setSheetName,\n                    {\n                        select_text : true,\n                        form_buttons : '',\n                        text_form: '<input type=\"text\" id=\"edit-#{id}\" class=\"#{editfield_class}\" value=\"#{value}\" /> ',\n                        after_save: pageView.updatePageTitle\n                    }\n                );\n            }\n\n            $(\"#id_grid_and_code\").splitter({\n                splitVertical: true,\n                outline: true,\n                anchorToWindow: true,\n                sizeLeft: true,\n                cookie: 'dirigible_vertical_splitter'\n            });\n            $(\"#id_right_column\").splitter({\n                splitHorizontal: true,\n                outline: true,\n                anchorToWindow: true,\n                sizeBottom: true,\n                cookie: 'dirigible_horizontal_splitter'\n            });\n        };\n\n        self.resizeComponents = function(event) {\n            $('#id_console').width($('#id_right_column').width() - 20);\n            $('#id_console').height($('#id_console_wrap').height() - 20);\n\n            var gridAndFormulaBarHeight = $('#id_left_column').height() - 10;\n            $('#id_grid_and_formula_bar').height(gridAndFormulaBarHeight);\n            $('#id_grid').height(gridAndFormulaBarHeight - $('#id_formula_bar').height() - 18);\n            $('#id_formula_bar').width($('#id_grid').width() - 8);\n            grid.resizeCanvas();\n\n            if ($('#id_usercode_wrap').height() < 30) {\n                $('#id_usercode').hide();\n            } else {\n                $('#id_usercode').show();\n            }\n            $('#id_usercode').width($('#id_usercode_wrap').width() - 20);\n            $('#id_usercode').height($('#id_usercode_wrap').height() - 10);\n            editor.resize();\n        };\n\n\n        self.bind = function() {\n            // Click-away handlers\n            editor.addEventListener('click', self.commitCurrentEdit);\n            $('#id_right_column, #id_header_and_toolbar').bind(\n                'click', self.commitCurrentEdit\n            );\n\n            editor.addEventListener('blur', editorCommands.saveUsercode);\n            editor.usercodeDirty = false;\n            editor.getSession().addEventListener(\n                'change', function() {\n                    editor.usercodeDirty = true;\n                });\n\n            // Ctrl-S always saves usercode.\n            // and F9 recalculates\n            $(document).keydown(\n                function(event) {\n                    if (event.ctrlKey && event.which === 83) {\n                        editorCommands.saveUsercode();\n                        event.preventDefault();\n                    } else if (event.which === 120) {\n                        pageCommands.recalculate();\n                        event.preventDefault();\n                    }\n                }\n            );\n\n            $(document).mouseup(function() {\n                window.setTimeout(self.resizeComponents, 0);\n            });\n            $(window).resize(function(event) {\n                window.setTimeout(self.resizeComponents, 0);\n            });\n        };\n\n    }\n\n})(jQuery);\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/scripts/page_view.js",
    "content": "// Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n// See LICENSE.md\n//\n\n(function($) {\n    $.extend(true, window, {\n        \"Dirigible\": {\n            \"PageView\": PageView\n        }\n    });\n\n    function PageView(username, consoleView, usercodeView, gridView) {\n        var self = this;\n\n        self.updateMetaData = function(sheetMetaData) {\n            if (sheetMetaData === null || sheetMetaData === undefined) {\n                return;\n            }\n\n            consoleView.updateMetaData(sheetMetaData);\n            usercodeView.updateMetaData(sheetMetaData);\n            gridView.updateMetaData(sheetMetaData);\n\n            $('#id_sheet_name').text(sheetMetaData.name);\n            self.updatePageTitle();\n        };\n\n        self.updatePageTitle = function() {\n            document.title = username + \"'s \" +\n                             $('#id_sheet_name').text() +\n                             \": Dirigible\";\n        };\n\n    }\n\n})(jQuery);\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/scripts/security_settings.js",
    "content": "// Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n// See LICENSE.md\n//\n\n(function($) {\n    $.extend(true, window, {\n        \"Dirigible\": {\n            \"SecuritySettings\": {\n                \"Dialog\" : Dialog,\n                \"Model\" : Model\n            }\n        }\n    });\n\n\n    function Model($, urls) {\n        var self = this;\n\n\n        self.updatePageUI = function() {\n            var securityButton = $('#id_security_button');\n            var title_text = 'Sheet security settings (';\n            if (self.isPublic) {\n                title_text += 'Sheet is public. ';\n            } else {\n                title_text += 'Sheet is private. ';\n            }\n            if (self.enabled) {\n                title_text += 'JSON API enabled)';\n            } else {\n                title_text += 'JSON API disabled)';\n            }\n            securityButton.attr('alt', title_text);\n            securityButton.attr('title', title_text);\n        };\n\n        self.updateServerState = function(\n            publicSheetCheckboxState,\n            jsonAPICheckboxState,\n            jsonAPIKeyFieldValue,\n            successHandler,\n            errorHandler\n        ) {\n            $.ajax({\n                type: \"POST\",\n                url: urls.setSecuritySettings,\n                data: {\n                    'is_public': publicSheetCheckboxState,\n                    'allow_json_api_access': jsonAPICheckboxState,\n                    'api_key': jsonAPIKeyFieldValue\n                },\n                success: function(data) {\n                    self.handleServerUpdateResponse(\n                        data,\n                        publicSheetCheckboxState,\n                        jsonAPICheckboxState,\n                        jsonAPIKeyFieldValue,\n                        successHandler,\n                        errorHandler\n                    );\n                },\n                error: errorHandler\n            });\n        };\n\n        self.handleServerUpdateResponse = function(data,\n            publicSheetCheckboxState, jsonAPICheckboxState,\n            jsonAPIKeyFieldValue, successHandler, errorHandler)\n        {\n            if (data === 'OK') {\n                successHandler();\n                self.isPublic = publicSheetCheckboxState;\n                self.enabled = jsonAPICheckboxState;\n                self.apiKey = jsonAPIKeyFieldValue;\n                self.updatePageUI();\n            } else {\n                errorHandler();\n            }\n        };\n\n    }\n\n\n    function Dialog($, urls) {\n        var self = this;\n\n        self.show = function(model) {\n            self.model__ = model;\n            $('#id_security_form_save_error').addClass('hidden');\n            self.updateDialogUI(model.isPublic, model.enabled, model.apiKey);\n            $('#id_security_form').dialog({ width: 400 });\n        };\n\n        self.updateDialogUI = function(isPublic, enabled, apiKey) {\n            $('#id_security_form_json_enabled_checkbox').attr('checked', enabled);\n            $('#id_security_form_public_sheet_checkbox').attr('checked', isPublic);\n            $apiKeyField = $('#id_security_form_json_api_key');\n            $apiUrlField = $('#id_security_form_json_api_url');\n            self.enableJsonApiFields_(enabled);\n            $apiKeyField.val(apiKey);\n            self.updateAPIURL();\n        };\n\n        self.enableJsonApiFields_ = function(enabled) {\n            if (enabled){\n                $apiKeyField.attr('disabled', '');\n                $apiUrlField.attr('disabled', '');\n            } else {\n                $apiKeyField.attr('disabled', 'disabled');\n                $apiUrlField.attr('disabled', 'disabled');\n            }\n        };\n\n        self.updateAPIURL = function() {\n            var newApiUrl = 'http://' + window.location.host\n                + urls.apiBase + '?api_key=' +\n                $('#id_security_form_json_api_key').val();\n            $('#id_security_form_json_api_url').val(newApiUrl);\n        };\n\n        self.updateDialogUIFromCheckboxes = function() {\n            var checkboxJSON = $('#id_security_form_json_enabled_checkbox');\n            var stateJSON = checkboxJSON.attr('checked');\n            var checkboxPublic = $('#id_security_form_public_sheet_checkbox');\n            var statePublic = checkboxPublic.attr('checked');\n            self.enableJsonApiFields_(stateJSON);\n        };\n\n        self.onOK = function() {\n            var publicCheckboxState = $('#id_security_form_public_sheet_checkbox').attr('checked');\n            var jsonAPICheckboxState = $('#id_security_form_json_enabled_checkbox').attr('checked');\n            var jsonAPIKeyFieldValue = $('#id_security_form_json_api_key').val();\n            self.model__.updateServerState(\n                publicCheckboxState,\n                jsonAPICheckboxState,\n                jsonAPIKeyFieldValue,\n                self.close,\n                self.handleSaveError\n            );\n            return false;\n        };\n\n        self.close = function() {\n            $('#id_security_form').dialog(\"close\");\n        };\n\n        self.handleSaveError = function() {\n            $('#id_security_form_save_error').removeClass('hidden');\n        };\n\n        self.selectFullURL_ = function() {\n            $(this).caret(0, $(this).val().length);\n        };\n\n        self.bind = function(model) {\n\n            $('#id_security_form_ok_button').click(self.onOK);\n            $('#id_security_form_cancel_button').click(self.close);\n\n            $('#id_security_form_json_api_key').bind(\n                'keyup mouseup change select',\n                self.updateAPIURL\n            );\n\n            $('#id_security_form_json_enabled_checkbox').click(self.updateDialogUIFromCheckboxes);\n\n            $('#id_security_form_json_api_url').click(self.selectFullURL_);\n            $('#id_security_form_json_api_key').click(self.selectFullURL_);\n        };\n\n    }\n\n})(jQuery);\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/scripts/selection_model.js",
    "content": "// Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\r\n// See LICENSE.md\r\n//\r\n\r\n(function($) {\r\n    // register namespace\r\n    $.extend(true, window, {\r\n        Dirigible: {\r\n            SelectionModel: SelectionModel\r\n        }\r\n    });\r\n\r\n\r\n\r\n    function SelectionModel() {\r\n        this.selectors = [\r\n            new Slick.CellRangeSelector(),\r\n            new Dirigible.KeyboardCellRangeSelector()\r\n        ];\r\n        this.onSelectedRangesChanged = new Slick.Event();\r\n        this.ranges = [];\r\n    }\r\n\r\n\r\n    SelectionModel.prototype.init = function(grid) {\r\n        this.grid = grid;\r\n        this.handleActiveCellChanged = this.getHandleActiveCellChanged();\r\n        this.grid.onActiveCellChanged.subscribe(this.handleActiveCellChanged);\r\n\r\n        this.handleBeforeCellRangeSelected = this.getHandleBeforeCellRangeSelected();\r\n        this.handleCellRangeSelected = this.getHandleCellRangeSelected();\r\n        for (var i=0; i < this.selectors.length; i++) {\r\n            var selector = this.selectors[i];\r\n            this.grid.registerPlugin(selector);\r\n            selector.onBeforeCellRangeSelected.subscribe(this.handleBeforeCellRangeSelected);\r\n            selector.onCellRangeSelected.subscribe(this.handleCellRangeSelected);\r\n        }\r\n    };\r\n\r\n\r\n    SelectionModel.prototype.destroy = function() {\r\n        this.grid.onActiveCellChanged.unsubscribe(this.handleActiveCellChanged);\r\n        for (var i=0; i < this.selectors.length; i++) {\r\n            var selector = this.selectors[i];\r\n            selector.onBeforeCellRangeSelected.unsubscribe(this.handleBeforeCellRangeSelected);\r\n            selector.onCellRangeSelected.unsubscribe(this.handleCellRangeSelected);\r\n            this.grid.unregisterPlugin(selector);\r\n        }\r\n    };\r\n\r\n\r\n    SelectionModel.prototype.getSelectedRanges = function() {\r\n        return this.ranges;\r\n    };\r\n\r\n\r\n    SelectionModel.prototype.setSelectedRanges = function(ranges) {\r\n        this.ranges = ranges;\r\n        this.onSelectedRangesChanged.notify(this.ranges);\r\n    };\r\n\r\n\r\n    SelectionModel.prototype.getHandleActiveCellChanged = function() {\r\n        var _self = this;\r\n        return function(event, cell) {\r\n            var activeCell = _self.grid.getActiveCell();\r\n            _self.setSelectedRanges([new Slick.Range(\r\n                    activeCell.row, activeCell.cell,\r\n                    activeCell.row, activeCell.cell)]);\r\n        };\r\n    };\r\n\r\n\r\n    SelectionModel.prototype.getHandleBeforeCellRangeSelected = function() {\r\n        var _self = this;\r\n        return function(event, cell) {\r\n            if (_self.grid.canCellBeSelected(cell.row, cell.cell)) {\r\n                _self.grid.getEditController().commitCurrentEdit();\r\n                _self.grid.setActiveCell(cell.row, cell.cell);\r\n            }\r\n            return true;\r\n        };\r\n    };\r\n\r\n\r\n    SelectionModel.prototype.getHandleCellRangeSelected = function() {\r\n        var _self = this;\r\n        return function(event, args) {\r\n            _self.setSelectedRanges([args.range]);\r\n        };\r\n    };\r\n\r\n\r\n})(jQuery);\r\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/scripts/sheet_page_utils.js",
    "content": "(function($) {\n    // register namespace\n    $.extend(true, window, {\n        \"Dirigible\": {\n            \"SheetUtils\": {\n                \"Initialise\": InitialiseSheetUtils\n            }\n        }\n    });\n\n\n    function InitialiseSheetUtils(urls, pageView) {\n        var _self = this;\n\n        function createNewGrid() {\n        }\n\n\n        function queueRecalculation() {\n            $.ajaxq('recalculation_queue', {\n                type: 'get',\n                url: urls.calculate,\n                success: getMetaData\n            });\n        }\n\n\n        function abortOtherRecalculations() {\n            $.ajaxq('recalculation_queue');\n        }\n\n\n        function getMetaData(content) {\n            if (content === 'OK') {\n                $.ajaxq('get_json_queue');\n                $.ajaxq('get_json_queue', {\n                    type: 'get',\n                    dataType: 'json',\n                    url: urls.getJSONMetaData,\n                    success: pageView.updateMetaData\n                });\n            }\n        }\n\n\n        $.extend(true, window, {\n            \"Dirigible\": {\n                \"SheetUtils\": {\n                    \"createNewGrid\": createNewGrid,\n                    \"queueRecalculation\": queueRecalculation,\n                    \"getMetaData\": getMetaData,\n                    \"abortOtherRecalculations\": abortOtherRecalculations\n                }\n            }\n        });\n\n    }\n\n})(jQuery);\n\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/scripts/toolbar_interaction_handler.js",
    "content": "(function($) {\n    // register namespace\n    $.extend(true, window, {\n        \"Dirigible\": {\n            \"ToolbarInteractionHandler\": ToolbarInteractionHandler\n        }\n    });\n\n    function ToolbarInteractionHandler(gridCommands, pageCommands) {\n        var self = this;\n        self.bind = function() {\n            $('#id_cut_button').click(gridCommands.cut);\n            $('#id_copy_button').click(gridCommands.copy);\n            $('#id_paste_button').click(gridCommands.paste);\n            $('#id_recalc_button').click(pageCommands.recalculate);\n        };\n    }\n\n})(jQuery);\n\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/scripts/usercode_view.js",
    "content": "// Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n// See LICENSE.md\n//\n\n(function($) {\n    $.extend(true, window, {\n        \"Dirigible\": {\n            \"UsercodeView\": UsercodeView\n        }\n    });\n\n    function UsercodeView(editor) {\n        var self = this;\n\n        self.initialiseComponents = function(editable) {\n            var PythonMode = require(\"ace/mode/python\").Mode;\n            editor.getSession().setMode(new PythonMode());\n            editor.setReadOnly(!editable);\n        };\n\n        self.updateMetaData = function(sheetMetaData) {\n            var annotations = [];\n            if ('usercode_error' in sheetMetaData) {\n                annotations.push( {\n                    row: parseInt(sheetMetaData.usercode_error.line) - 1,\n                    column: 0,\n                    text: sheetMetaData.usercode_error.message,\n                    type: \"error\"\n                } );\n            }\n            editor.getSession().setAnnotations(annotations);\n        };\n\n    }\n\n})(jQuery);\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/styles/base.css",
    "content": "html {\n    height: 100%;\n}\n\n\nbody {\n    height: 100%;\n    margin: 0;\n    padding: 0;\n    font-family: Helvetica, Arial, sans-serif;\n    background: #009EE0;\n    color: #FFFFFF;\n}\n\n\na {\n    text-decoration: None;\n    color: #FFFFFF;\n}\n\na:hover {\n    text-decoration: underline;\n}\n\n.blue_button {\n    color: #FFFFFF;\n    background-color: #016AA1;\n    border: 0px;\n    margin: 0px;\n\n}\ninput[type=submit], input[type=button], a.button {\n}\n\na.button:hover {\n    text-decoration: none;\n}\n\n\na img {\n    border: 0px;\n}\n\n\n/* Separators between (eg.) \"My account\" and \"Log out\" */\n.separator {\n    color: #5CBEE5;\n    margin-left: 0.25em;\n    margin-right: 0.25em;\n}\n\ndiv.clear {\n    clear: both\n}\n\n.hidden {\n    display: none;\n}\n\n.dialog_buttons {\n    float: right;\n}\n\n#id_small_header_logo {\n    margin: 15px 0px 8px 10px;\n    float: left;\n}\n\n\n#id_top_links {\n    float: right;\n    clear: right;\n    margin-top: 10px;\n    margin-right: 16px;\n    font-size: 14pt;\n    font-weight: bold;\n}\n\n\n#id_account_buttons_wrap {\n    padding-top: 14px;\n    padding-bottom: 11px;\n    float: right;\n    background-color: #006AA0;\n    font-weight: bold;\n    padding-left: 10px;\n    padding-right: 10px;\n}\n\n#id_feedback_dialog_blurb_big {\n    font-weight: bold;\n    font-size: larger;\n    margin-bottom: 0.25em;\n    margin-top: 0.25em;\n}\n\n#id_feedback_dialog_text {\n    margin-top: 1em;\n    margin-bottom: 1em;\n    width: 562px;\n}\n\n#id_feedback_dialog_email_address {\n    width: 562px;\n}\n\n.email_prompt {\n    font-style: italic;\n    color: #808080;\n}\n\n#id_feedback_dialog .dialog_buttons {\n    margin-top: 1.5em;\n}\n\n#id_feedback_dialog_error {\n    font-size: smaller;\n    margin-top: 3ex;\n    float: left;\n}\n\n#id_feedback_dialog_spinner {\n    margin-right: 1ex;\n}\n\n#id_feedback_dialog_ok_button {\n    font-weight: bold;\n}\n\n/* JQuery UI style overrides */\n.ui-widget-content a {\n    color: #009EE0 !important;\n}\n\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/styles/coming_soon_page.css",
    "content": "#id_coming_soon {\n    margin-top: 35px;\n    margin-bottom: 35px;\n    font-size: 28px;\n    color: #4B576D;\n    text-align: center;\n    height: 700px;\n}\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/styles/contact.css",
    "content": "p.address {\r\n    margin-left: 2ex;\r\n}\r\n\r\n\r\np.regulatory {\r\n    clear: both; \r\n    padding-top: 20px; \r\n    font-size: smaller;\r\n}\r\n\r\n\r\ndiv.map {\r\n    float: right; \r\n    margin: 0 2em 0.5em 0.5em; \r\n    padding: 0.3em; \r\n    border: 1px solid lightgray;\r\n}"
  },
  {
    "path": "dirigible/shared/static/dirigible/styles/error.css",
    "content": "#id_error_wrap {\r\n    width: 29em;\r\n    clear: both;\r\n    margin-top: 4em;\r\n    margin-left: auto;\r\n    margin-right: auto;\r\n    padding-top: 1.5em;\r\n    padding-bottom: 1.5em;\r\n    font-size: 14pt;\r\n    text-align: center;\r\n}\r\n\r\n#id_recovery_block a {\r\n    color: #FFFFFF;\r\n    font-weight: bold;\r\n}"
  },
  {
    "path": "dirigible/shared/static/dirigible/styles/index.css",
    "content": ".id_dirigible_tagline {\n    font-size: 20px;\n    color: #4B576D;\n    text-align: center;\n}\n\n#id_grey_stripe_hwrap {\n    background-color: #D1D2D4;\n    padding-top: 40px;\n    padding-bottom: 40px;\n}\n\n#id_more_info_buttons {\n    /* Only easy to center a block if its width is fixed :-(  Luckily this one contains\n       images so we know how wide it is. */\n    width: 470px;\n    margin-left: auto;\n    margin-right: auto;\n    margin-top: 35px;\n}\n\n#id_more_info_buttons a img {\n    border: 0px;\n}\n\n#id_find_out_more {\n    margin: 0px 5px 0px 0px;\n    padding: 0px;\n}\n\n#id_watch_a_video {\n    margin: 0px 0px 0px 5px;\n    padding: 0px;\n}\n\n#id_featured_sheets_front_page {\n    margin-top: 30px;\n}\n\n#id_featured_sheets_front_page a {\n    color: #4B576D;\n    text-decoration: underline;\n}\n\n#id_signup_call_to_action_div {\n    font-size: 36pt;\n    margin-top: 35px;\n    text-align: center;\n    width: 11.5ex;\n    border-radius: 24px;\n    -moz-border-radius: 24px;\n}\n\n#id_signup_call_to_action {\n}\n\n#id_footer_links {\n    position: relative;\n    bottom: 0;\n}\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/styles/info_page.css",
    "content": "#id_extra_banner_spacing {\n    clear: both; \n    margin-bottom: 10px;\n}\n\nh1 {\n    padding-top: 0px;\n    margin-top: 0px;\n}\n\n#id_main_background {\n    padding-bottom: 0px;\n    padding-top: 0px;\n}\n\n#id_content_background {\n    width: 630px;\n    background-color: #FFFFFF;\n    padding: 40px;\n}\n\n#id_text {\n    color: #4B576D;\n}\n\n#id_text a {\n    color: #009EE0;\n}\n\nimg.screenshot {\n    text-align: center;\n    margin: 10px 42px 0px 42px;\n}\n\nhr {\n    width: 5em;\n    margin-top: 20px;\n    margin-bottom: 20px;\n}\n\n/* Featured sheet page-specific class styles */\nh2.featured_sheet_name {\n    margin-bottom: 0;\n}\n\n.featured_sheet_actions {\n    float:right;\n    text-align: center;\n    font-size: smaller;\n    padding-top: 5px;\n}\n\n#id_text .featured_sheet_actions a {\n    color: #006AA0;\n}\n\n.featured_sheet_actions span {\n    background-color: #D1D2D4;\n    padding: 5px;\n    margin-left: 5px;\n    border-radius: 6px;\n    -moz-border-radius: 6px;\n}\n\n.featured_sheet_description {\n    margin-bottom: 1.5em;\n}\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/styles/login.css",
    "content": "#id_login_form_wrap {\n    width: 29em;\n    clear: both;\n    margin-top: 4em;\n    margin-left: auto;\n    margin-right: auto;\n    padding-top: 1.5em;\n    padding-bottom: 1em;\n    font-size: 14pt;\n}\n\n#id_login_form_header {\n    margin-left: 20px;\n    margin-right: 20px;\n    margin-bottom: 30px;\n}\n\n\n#id_login_signup_blurb {\n    font-size: 12pt;\n}\n\n\n#id_login_signup_blurb a {\n    text-decoration: underline;\n}\n\n#id_login_form {\n    text-align: center;\n}\n\n#id_username_wrap {\n    clear: both;\n}\n\n#id_username_label {\n    text-align: right;\n    vertical-align: baseline;\n    margin-right: 0.5em;\n    width: 6em;\n    font-weight: bold;\n}\n\n#id_username {\n    text-align: left;\n    vertical-align: baseline;\n    width: 20em;\n    margin-bottom: 1ex;\n    font-size: 14pt;\n}\n\n#id_password_wrap {\n    clear: both;\n}\n\n#id_password_label {\n    text-align: right;\n    vertical-align: baseline;\n    margin-right: 0.5em;\n    width: 6em;\n    font-weight: bold;\n}\n\n#id_password {\n    text-align: left;\n    vertical-align: baseline;\n    width: 20em;\n    margin-bottom: 1ex;\n    font-size: 14pt;\n}\n\n#id_login {\n    clear: both;\n    margin-top: 1em;\n    padding: 0.5em;\n    font-weight: bold;\n    font-size: 14pt;\n}\n\n#id_login_error {\n    margin-top: 0px;\n}\n\nh1 {\n    text-align: left;\n    margin-top: 0px;\n}\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/styles/non_sheet_page.css",
    "content": ".centered-block {\r\n    width: 710px;\r\n    clear: both;\r\n    margin-left: auto;\r\n    margin-right: auto;\r\n}\r\n#id_ie_warning {\r\n    padding: 4px;\r\n    margin-bottom: 10px;\r\n}\r\n\r\n#id_ie_warning a{\r\n    text-decoration: underline;\r\n}\r\n\r\n.grey_rounded_corner_box {\r\n    border: 5px solid #FFFFFF;\r\n    border-radius: 12px;\r\n    -moz-border-radius: 12px;\r\n    background-color: #D1D2D4;\r\n    color: #4B576D;\r\n    padding: 15px;\r\n}\r\n\r\n.grey_rounded_corner_box a {\r\n    color: #4B576D;\r\n}\r\n\r\n.grey_rounded_corner_box a.button {\r\n    color: #FFFFFF;\r\n}\r\n\r\n#id_container {\r\n    position: relative;\r\n    min-height: 100%;\r\n}\r\n\r\n#id_content {\r\n    padding-bottom: 9em;\r\n}\r\n\r\n#id_big_logo_wrap {\r\n    margin-top: 8px;\r\n    margin-bottom: 28px;\r\n}\r\n\r\n#id_big_logo {\r\n    display: block;\r\n    margin-left: auto;\r\n    margin-right: auto;\r\n}\r\n\r\n#id_footer_links {\r\n    margin-top: 30px;\r\n    color: #FFFFFF;\r\n    text-align: center;\r\n    font-size: 13px;\r\n    width: 100%;\r\n    position: absolute;\r\n    bottom: 7em;\r\n}\r\n\r\n#id_about_us {\r\n    margin-bottom: 5px;\r\n}\r\n\r\n\r\n#id_copyright_wrap {\r\n    background-color: #4B576D;\r\n    color: #FFFFFF;\r\n    padding-top: 20px;\r\n    height: 5em;\r\n    width: 100%;\r\n    text-align: center;\r\n    font-size: 13px;\r\n    position: absolute;\r\n    bottom: 0px;\r\n}\r\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/styles/pricing.css",
    "content": "#id_beta_free_splash {\r\n    background-color: #FFEB8C;\r\n}\r\n\r\n\r\n#id_price_plan_table {\r\n    border-spacing: 20px;\r\n}\r\n\r\n#id_price_plan_table td {\r\n    background-color: #99DDFF;\r\n    border-radius: 12px;\r\n    -moz-border-radius: 12px;\r\n    border: 2px solid #009EE0;\r\n    padding: 20px;\r\n    font-size: 12px;\r\n}\r\n\r\n#id_tax {\r\n    font-size: smaller;\r\n}\r\n\r\n\r\n.plan_cell {\r\n    width: 25%;\r\n    vertical-align: top;\r\n}\r\n\r\n\r\n.plan_cell ul {\r\n    padding-left: 15px;\r\n    margin: 0;\r\n}\r\n\r\n\r\n.plan_header {\r\n    margin-bottom: 7px;\r\n}\r\n\r\n\r\n.plan_name {\r\n    font-size: 18px;\r\n}\r\n\r\n\r\n.plan_price {\r\n    margin-top: 4px;\r\n    font-size: 14px;\r\n    float: right;\r\n}"
  },
  {
    "path": "dirigible/shared/static/dirigible/styles/registration.css",
    "content": "#id_signup_vwrap {\r\n    margin-top: 3em;\r\n}\r\n\r\n\r\n#id_signup_hwrap {\r\n    width: 30em;\r\n    clear: both;\r\n    margin-left: auto;\r\n    margin-right: auto;\r\n    padding: 1.5em 2em;\r\n}\r\n\r\n#id_register_intro_text {\r\n    text-align: center;\r\n    margin-bottom: 20px;\r\n}\r\n\r\ntd.label {\r\n    font-size: 20px;\r\n    text-align: right;\r\n    width: 35%;\r\n    padding-right: 5px;\r\n}\r\n\r\n\r\ntd.input {\r\n    width: 60%;\r\n}\r\ntd.input input{\r\n    font-size: 20px;\r\n    border: 1px solid #888888;\r\n    width: 95%;\r\n    padding: 4px;\r\n}\r\n\r\n#id_signup_button {\r\n    margin-top: 20px;\r\n    padding-left: 10px;\r\n    padding-right: 10px;\r\n    padding-top: 2px;\r\n    padding-bottom: 2px;\r\n    height: 31pt;\r\n    font-size: 20px;\r\n    font-weight: bold;\r\n}\r\n\r\n#id_signup_table {\r\n    width: 100%;\r\n}\r\n\r\ntd {\r\n    text-align: right;\r\n}\r\n\r\nul.errorlist {\r\n    list-style-type: none;\r\n    margin: 1em 0 0 0;\r\n    color: #880000;\r\n}\r\n\r\n#id_password1.error, #id_password2.error {\r\n    background-color: #FFDDDD;\r\n}\r\n\r\n#id_signup_form {\r\n    margin-top: 1em;\r\n    margin-bottom: 1em;\r\n}\r\n\r\n#id_signup_hwrap p#id_link_home_para {\r\n    margin-top: 1em; !important\r\n}\r\n\r\n#id_link_home {\r\n    color: #FFFFFF;\r\n    font-weight: bold;\r\n}\r\n\r\n#id_login_form {\r\n    text-align: center;\r\n}\r\n\r\n#id_we_dont_spam {\r\n    text-align: center;\r\n    margin-top: 20px;\r\n}\r\n\r\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/styles/sheet_page.css",
    "content": "body {\n    /* prevents splitter resizing that sometimes leaves space for\n       scrollbars that are not there. This does mean that there\n       are no scrollbars when the grid, code pane and error console\n       are larger than the window */\n    overflow: hidden;\n}\n\n#id_header_and_toolbar {\n    /* We explicitly set the header height here so that Chrome\n       doesn't miscalculate the height and cause the bottom\n       of the grid to fall off the window */\n}\n\n#id_ie_warning {\n    background: #444;\n    margin-top: 13px;\n    margin-right: 13px;\n    padding: 1px 2px;\n    float: right;\n}\n\n\n#id_copy_sheet_div {\n    padding-top: 45px;\n    margin-left: auto;\n    margin-right: auto;\n    width: 500px;\n    text-align: center;\n}\n\n\n#id_copy_sheet_div a {\n    text-decoration: underline;\n}\n\n\n#id_sheet_name_wrap {\n    width: 50%;\n    float: left;\n}\n\n#id_sheet_name {\n    margin-left: 10px;\n    font-size: 20pt;\n    margin-right: 10px;\n    overflow: hidden;\n    height: 2ex;\n}\n\n\n#edit-id_sheet_name {\n    margin-left: 6px;\n    margin-right: 10px;\n    font-size: 20pt;\n    width: 97%;\n}\n\n.jeip-mouseover {\n    background: #D1D2D4;\n}\n\n\n/* The icon toolbar */\n#id_toolbar_wrap {\n    width: 50%;\n    float: right;\n}\n\n#id_toolbar {\n    padding-left: 20px;\n    padding-right: 20px;\n    text-align: right;\n}\n\n/* Separators above and below the icon toolbar */\n.toolbar_line {\n    border: 0 none;\n    padding: 0 0 0 50px;\n    height: 1px;\n    background-color: #5cbee5;\n    color: #5cbee5;\n}\n\n#id_toolbar_buttons {\n    float: right;\n}\n\n#id_spinner_image {\n    float: left;\n    height: 32px;\n}\n\n#id_buffering_message {\n    position: absolute;\n    font-weight: bold;\n    font-size: small;\n    left: 100px;\n    top: 70px;\n    font-family: monospace;\n}\n\n#id_grid_and_code {\n    /* splitter */\n    min-height: 400px;\n}\n\n#id_left_column a {\n    color: #49546A;\n}\n\n#id_left_column {\n    color: #49546a;\n    margin: 0;\n    width: 60%;\n    /* splitter */\n    overflow: hidden;\n}\n\n#id_right_column {\n    background: #009EE0;\n    color: #ffffff;\n    margin: 0;\n    padding: 0;\n    border: 0;\n    /* splitter */\n    overflow: hidden;\n}\n\n/* Usercode editor */\n#id_usercode_wrap {\n    position: absolute; \n    top: 0; \n    left: 10px; \n    width: 100%; \n    height: 100%;\n}\n\n#id_usercode {\n    background-color: white;\n    position: absolute; \n    top: -12px; \n    left: 10; \n}\n\n.user_code .heading {\n    background: #2E82AE;\n}\n\n/* Error console */\n#id_console_wrap {\n    height: 100px;\n}\n\n#id_console {\n    white-space: pre;\n    overflow: auto;\n    background: white;\n    color: black;\n    margin-top: 10px;\n    margin-left: 10px;\n}\n\n.console_error_text {\n    color: #ff0000;\n}\n\n.console_output_text {\n    color: #000000;\n}\n\n.console_system_text {\n    font-style: italic;\n    color: #000000;\n}\n\n#id_formula_bar {\n    margin: 0 10px 10px 11px;\n    padding: 3px;\n    border: 1px solid #888888;\n}\n\n/* SlickGrid stuff */\n#id_grid {\n    padding-left: 10px;\n    padding-right: 10px;\n}\n\n.grid-canvas {\n    background: #ffffff;\n}\n\n.c0 {\n    color: #ffffff;\n    text-align: right;\n}\n\n.even .c0 {\n    background: #2E82AE;\n    border-color: #2E82AE;\n}\n\n.odd .c0 {\n    background: #1a6e9a;\n    border-color: #1a6e9a;\n}\n\n.slick-header-columns div {\n    background: #2E82AE;\n    color: #ffffff;\n    text-align: center;\n}\n\n.slick-header-column.ui-state-default {\n    border-right: 0 !important;\n}\n\n.slick-cell {\n    font-size: 12pt;\n}\n\n.slick-cell.active {\n    margin-left: 0;\n    margin-top: 0;\n    border: solid gray 1px;\n}\n\n.slick-cell.selected {\n    background-color : #C8E9F7;\n}\n\n.slick-cell.active.selected {\n    background-color : #FFFFFF;\n}\n\ninput.editor-text {\n    background:none repeat scroll 0 0 transparent;\n    border:0 none;\n    height:100%;\n    margin:0;\n    outline:0 none;\n    padding:0;\n    width:100%;\n    font-size: 12pt; \n}\n\n.grid_formula {\n    color: #BBBBBB;\n}\n\n/* Splitter */\n.vsplitbar {\n    width: 6px;\n    background: #80CFF0 url(/static/splitter/img/vgrabber.png) no-repeat center;\n}\n.hsplitbar {\n    height: 6px;\n    background: #80CFF0 url(/static/splitter/img/hgrabber.png) no-repeat center;\n}\n\n/* Copyright and \"extra links\" stuff at the bottom of the page */\n.column_footer {\n    position: absolute;\n    bottom: 0;\n    padding-left: 20px;\n}\n\n\n#id_tutorial_promo_dialog_close {\n    margin-top: 1ex;\n    font-size: large;\n}\n\n#id_import_form form {\n    text-align: right;\n}\n\n#id_import_form_upload_csv_button {\n    font-weight: bold;\n}\n\n#id_import_form_upload_xls_values_button {\n    font-weight: bold;\n}\n\n#id_security_form {\n}\n\n#id_security_form form {\n}\n\n#id_security_form_general_settings {\n    margin-bottom: 2ex;\n}\n\n#id_security_form_api_settings {\n    margin-bottom: 1ex;\n}\n\n#id_security_form_api_subsettings {\n    margin-top: 1ex;\n    margin-left: 2em;\n}\n\n#id_security_form_public_sheet_help {\n    font-size: smaller;\n    margin-left: 2em;\n}\n\n#id_security_form_json_help {\n    font-size: smaller;\n    margin-left: 2em;\n}\n\n#id_security_form_ok_button {\n    font-weight: bold;\n}\n\n#id_security_form_json_api_key {\n    width: 200px;\n}\n\n#id_security_form_json_api_url {\n    width: 200px;\n}\n\n#id_security_form_save_error {\n    font-size: smaller;\n    margin-top: 3ex;\n}\n\n\ntd.label {\n}\n\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/styles/user_page.css",
    "content": "#id_dashboard_wrap {\n    margin-top: 2em;\n}\n\n\n#id_sheets {\n    width: 413px;\n    float: left;\n}\n\n#id_right_column {\n    float: right;\n}\n\n#id_account_details {\n    width: 187px;\n    border: 5px solid #4B576D;\n    border-radius: 12px;\n    -moz-border-radius: 12px;\n    background-color: #AEAFAF;\n    color: #4B576D;\n    padding: 15px;\n}\n\n#id_example_sheets {\n    width: 187px;\n    border: 5px solid #4B576D;\n    border-radius: 12px;\n    -moz-border-radius: 12px;\n    background-color: #AEAFAF;\n    color: #4B576D;\n    padding: 15px;\n    margin-top: 1em;\n}\n\n#id_create_new_sheet {\n    font-weight: bold;\n    float: right;\n}\n\n.section-title {\n    font-size: 14pt;\n    margin-top: 0px;\n}\n\n#id_sheets_title {\n    display: inline;\n}\n\n#id_sheets_body {\n    clear: both;\n}\n\n#id_no_sheets_message {\n    font-style: italic;\n    margin-left: 1em;\n}\n\n#id_change_password_button {\n    font-weight: bold;\n    margin-left: 2ex;\n}\n\n#id_change_password_form {\n    display: none;\n}\n\n#id_change_password_success {\n    display: none;\n    margin-top: 1em;\n    color: #008000;\n}\n\n#id_change_password_error {\n    margin-top: 1em;\n    color: #800000;\n}\n\n#id_change_password_buttons {\n    margin-top: 1em;\n    float: right;\n}\n\n#id_change_password_form input[type='password'] {\n    margin-bottom: 0.5em;\n}\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/styles/video.css",
    "content": "body {\r\n    background: #000000;\r\n}\r\n\r\n#id_video_div {\r\n    margin-left: -125px;\r\n    margin-top: 20px;\r\n}"
  },
  {
    "path": "dirigible/shared/static/dirigible/tests/cell_editor_test.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n<html>\n  <head>\n    <script type=\"text/javascript\" src=\"/static/json/json2.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/jquery/jquery-1.4.2.min.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/jquery/jquery-ui-1.8.10.custom.min.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/jquery/jquery.caret.1.02.min.js\"></script>\n\n    <script type=\"text/javascript\" src=\"yuitest/yuitest-combo.js\"></script>\n    <script type=\"text/javascript\" src=\"test_utils.js\"></script>\n\n    <script type=\"text/javascript\" src=\"../scripts/cell_editor.js\"></script>\n\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"logger.css\" />\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"testlogger.css\" />\n  </head>\n\n  <body>\n    <div id=\"id_results\">Please wait</div>\n\n    <script type=\"text/javascript\">\n\n        function create_div_and_editor_for(args) {\n            var $test_container = $(\"<div>\", { id: args.test_name + \"-div\" })\n                .appendTo($(\"#test-divs\"));\n            var formula_bar_id = args.test_name + \"-formula-bar\";\n            $test_container.append($(\"<input>\", { id: formula_bar_id }));\n            var formula_bar = $(\"#\" + formula_bar_id);\n\n            CellEditor = CellEditorFactory(formula_bar);\n\n            var editorArgs = jQuery.extend({ container : $test_container }, args.editor_args);\n            var cell_editor = new CellEditor(editorArgs);\n            return { test_container: $test_container, cell_editor: cell_editor, formula_bar: formula_bar };\n        }\n\n\n        function assertLoadValueCallsValWithFormulaAndSetsDefaultValueOnThisAndUnderlyingDOMElement(testName, item, expectedEditValue) {\n            var target = create_div_and_editor_for({\n                test_name: testName,\n                editor_args: {\n                    column: {\n                        field: \"column_field\"\n                    }\n                }\n            });\n            var valArgument = undefined;\n            target.cell_editor.$input.val = function(argument) {\n                if (argument !== undefined) {\n                    // We only care about cases when val was called with a parameter (ie. as a setter);\n                    // if our argument is undefined, then it was called as a getter and we don't really\n                    // care.\n                    valArgument = argument;\n                }\n            };\n\n            target.cell_editor.loadValue(item);\n            YAHOO.util.Assert.areSame(expectedEditValue, target.cell_editor.defaultValue, \"expected value not set as object's defaultValue\");\n            YAHOO.util.Assert.areSame(expectedEditValue, valArgument, \"val not called with expected value\");\n            YAHOO.util.Assert.areSame(\n                expectedEditValue,\n                target.cell_editor.$input[0].defaultValue,\n                \"defaultValue not set to expected value\"\n            );\n        }\n\n        tests = [\n\n            new YAHOO.tool.TestCase({\n\n                name: \"test_cell_editor\",\n\n\n                testConstructorAppendsDOMInputToProvidedContainer: function () {\n                    var target = create_div_and_editor_for({ test_name: \"testInitAppendsDOMInputToProvidedContainer\"});\n                    YAHOO.util.Assert.areSame(\"INPUT\", target.cell_editor.$input[0].tagName, \"Wrong tag name\");\n                    YAHOO.util.Assert.areSame(\"text\", target.cell_editor.$input[0].type, \"Wrong element type\");\n                    YAHOO.util.Assert.areSame(\"editor-text\", target.cell_editor.$input[0].className, \"Wrong class\");\n                    YAHOO.util.Assert.areSame(target.test_container[0], target.cell_editor.$input.parent()[0], \"Wrong parent\");\n                },\n\n\n                testConstructorSetsColumnField: function() {\n                    var expectedColumn = \"test stuff\";\n                    var target = create_div_and_editor_for({\n                        test_name: \"testConstructorSetsColumnField\",\n                        editor_args: {\n                            column: expectedColumn\n                        }\n                    });\n                    YAHOO.util.Assert.areSame(expectedColumn, target.cell_editor.column);\n                },\n\n\n                testConstructorBindsKeydownEvent : function () {\n                    var target = create_div_and_editor_for({ test_name: \"testConstructorBindsKeydownEvent\" });\n                    YAHOO.util.Assert.isFunction(target.cell_editor.$input.data('events').keydown[0].handler);\n                    YAHOO.util.Assert.areSame('keydown', target.cell_editor.$input.data('events').keydown[0].type);\n\n                    var stopImmediatePropagationCalled = false;\n                    e = {};\n                    e.stopImmediatePropagation = function () {\n                        stopImmediatePropagationCalled = true;\n                    };\n\n                    keydown_handler = target.cell_editor.$input.data('events').keydown[0].handler;\n\n                    LEFT = 37;\n                    stopImmediatePropagationCalled = false;\n                    e.keyCode = LEFT;\n                    keydown_handler(e);\n                    YAHOO.util.Assert.isTrue(stopImmediatePropagationCalled, 'event propagation not stopped for LEFT');\n\n                    RIGHT = 39;\n                    stopImmediatePropagationCalled = false;\n                    e.keyCode = RIGHT;\n                    keydown_handler(e);\n                    YAHOO.util.Assert.isTrue(stopImmediatePropagationCalled, 'event propagation not stopped for RIGHT');\n                },\n\n                testConstructorSetsFocusOnInputBoxIfFormulaBarNotFocussed: function () {\n                    var target = create_div_and_editor_for({ test_name: \"testConstructorSetsFocusOnInputBoxIfFormulaBarNotFocussed\" });\n                    YAHOO.util.Assert.areSame(\n                            target.cell_editor.$input[0],\n                            document.activeElement,\n                            \"Focus not set to input element on creation\");\n                },\n\n                testConstructorDoesntSetFocusOnInputBoxIfFormulaBarIsFocussed: function () {\n\n                    var formula_bar_id;\n\n                    function local_create_div_and_editor_for(args) {\n                        var $test_container = $(\"<div>\", { id: args.test_name + \"-div\" })\n                            .appendTo($(\"#test-divs\"));\n\n                        // our local customisations to this function\n                        formula_bar_id = args.test_name + \"-formula-bar\";\n                        $test_container.append($(\"<input>\", { id: formula_bar_id }));\n                        var formula_bar = $(\"#\" + formula_bar_id);\n                        formula_bar.focus();\n\n                        CellEditor = CellEditorFactory(formula_bar);\n\n                        var editorArgs = jQuery.extend({ container : $test_container }, args.editor_args);\n                        var cell_editor = new CellEditor(editorArgs);\n                        return { test_container: $test_container, cell_editor: cell_editor, formula_bar: formula_bar };\n                    }\n\n                    var target = local_create_div_and_editor_for(\n                        { test_name: \"testConstructorDoesntSetFocusOnInputBoxIfFormulaBarIsFocussed\" }\n                    );\n                    var formula_bar = $(\"#\" + formula_bar_id);\n                    YAHOO.util.Assert.areSame(\n                            formula_bar[0],\n                            document.activeElement,\n                            \"Focus should still be set to formula bar\");\n                },\n\n                testDestroyDelegatesToElementRemove: function () {\n                    var target = create_div_and_editor_for({ test_name: \"testDestroyRemovesElement\" });\n                    var remove_called = false;\n                    target.cell_editor.$input.remove = function() {\n                        remove_called = true;\n                    };\n                    target.cell_editor.destroy();\n                    YAHOO.util.Assert.isTrue(remove_called, \"Remove not called on underlying element\");\n                },\n\n\n                testLoadValueCallsValWithFormulaAndSetsDefaultValueOnUnderlyingDOMElement: function () {\n                    var expectedEditValue = \"this is the value to edit\";\n                    assertLoadValueCallsValWithFormulaAndSetsDefaultValueOnThisAndUnderlyingDOMElement(\n                        \"testLoadValueCallsValWithFormulaAndSetsDefaultValueOnUnderlyingDOMElement\",\n                        {\n                            column_field: {\n                                formula: expectedEditValue,\n                                formatted_value: \"this should never be seen\"\n                            }\n                        },\n                        expectedEditValue\n                    );\n                },\n\n\n                testLoadValueHandlesUndefinedObject : function () {\n                    var expectedEditValue = \"\";\n                    assertLoadValueCallsValWithFormulaAndSetsDefaultValueOnThisAndUnderlyingDOMElement(\n                        \"testLoadValueHandlesUndefinedObject\",\n                        undefined,\n                        expectedEditValue\n                    );\n                },\n\n\n                testLoadValueHandlesEmptyObject : function () {\n                    var expectedEditValue = \"\";\n                    assertLoadValueCallsValWithFormulaAndSetsDefaultValueOnThisAndUnderlyingDOMElement(\n                        \"testLoadValueHandlesEmptyObject\",\n                        {},\n                        expectedEditValue\n                    );\n                },\n\n\n                testLoadValueSetsFormulaBar : function() {\n                    var target = create_div_and_editor_for({\n                        test_name: \"testLoadValueSetsFormulaBar\",\n                        editor_args: {\n                            column: {\n                                field: \"column_field\"\n                            }\n                        }\n                    });\n\n                    var formula_bar_val_params = undefined;\n                    target.formula_bar.val = function(params) {\n                        if (params !== undefined) {\n                            // Ignore get-style calls to the formula bar's val function, we only\n                            // care about sets.\n                            formula_bar_val_params = params;\n                        }\n                    };\n\n                    var expectedEditValue = \"this is the value to edit\";\n                    target.cell_editor.loadValue({\n                        column_field: {\n                            formula: expectedEditValue,\n                            formatted_value: \"this should never be seen\"\n                        }\n                    });\n\n                    YAHOO.util.Assert.areSame(expectedEditValue, formula_bar_val_params);\n                },\n\n\n                testLoadValueCallsUpdateFormulaBarIfChanged: function() {\n                    var target = create_div_and_editor_for({\n                        test_name: \"testLoadValueCallsUpdateFormulaBarIfChanged\",\n                        editor_args: {\n                            column: {\n                                field: \"column_field\"\n                            }\n                        }\n                    });\n                    var uficCalled = false;\n                    target.cell_editor.updateFormulaBarIfChanged = function() {\n                        uficCalled = true;\n                    };\n\n                    target.cell_editor.loadValue({\n                        column_field: {\n                            formula: \"foo\",\n                            formatted_value: \"this should never be seen\"\n                        }\n                    });\n\n                    YAHOO.util.Assert.isTrue(uficCalled, 'loadValue should call updateFormulaBarIfChanged');\n                },\n\n                testSetInputElementValuesSetsInputValueAndCaretPositionAndCallsUpdateFormulaBarIfChanged: function() {\n                    var target = create_div_and_editor_for({\n                        test_name: \"testSetInputElementValuesSetsInputValueAndCaretPositionAndCallsUpdateFormulaBarIfChanged\",\n                        editor_args: {\n                            column: {\n                                field: \"column_field\"\n                            }\n                        }\n                    });\n                    var uficCalled = false;\n                    target.cell_editor.updateFormulaBarIfChanged = function() {\n                        uficCalled = true;\n                    };\n                    var caretStart = -1;\n                    var caretEnd = -1;\n                    target.cell_editor.$input.caret = function(start, end) {\n                        caretStart = start;\n                        caretEnd = end;\n                    };\n                    target.cell_editor.setInputElementValues('some stuff');\n\n                    YAHOO.util.Assert.areSame('some stuff', target.cell_editor.$input.val());\n\n                    YAHOO.util.Assert.areSame(10, caretStart);\n                    YAHOO.util.Assert.areSame(10, caretEnd);\n\n                    YAHOO.util.Assert.isTrue(uficCalled, 'setInputElementValues should call updateFormulaBarIfChanged');\n                },\n\n                testOnInputBoundFunctionCallsUpdateFormulaBarIfChanged: function() {\n                    var target = create_div_and_editor_for({\n                        test_name: \"testUpdateFormulaBarCallsUpdateFormulaBarIfChanged\",\n                        editor_args: {\n                            column: {\n                                field: \"column_field\"\n                            }\n                        }\n                    });\n                    var uficCalled = false;\n                    target.cell_editor.updateFormulaBarIfChanged = function() {\n                        uficCalled = true;\n                    };\n                    target.cell_editor._updateFormulaBar();\n                    YAHOO.util.Assert.isTrue(uficCalled, '_updateFormulaBar should call updateFormulaBarIfChanged');\n                },\n\n\n                testUpdateFormulaBarIfChangedWorks: function() {\n                    var target = create_div_and_editor_for({\n                        test_name: \"testUpdateFormulaBarIfChangedWorks\",\n                        editor_args: {\n                            column: {\n                                field: \"column_field\"\n                            }\n                        }\n                        });\n\n                    var call_count = 0;\n                    target.cell_editor.$formula_bar.val = function(arg){\n                        if (arg !== undefined) {\n                            call_count = call_count + 1;\n                        } else {\n                            return 'old value';\n                        }\n                    };\n                    YAHOO.util.Assert.areSame(0, call_count);\n                    target.cell_editor.updateFormulaBarIfChanged('old value');\n                    YAHOO.util.Assert.areSame(0, call_count);\n\n                    target.cell_editor.updateFormulaBarIfChanged('changed value');\n                    YAHOO.util.Assert.areSame(1, call_count);\n                },\n\n\n                testSerializeValueReturnsCellDict : function () {\n                    var target = create_div_and_editor_for({\n                        test_name: \"testSerializeValueReturnsCellDict\",\n                        editor_args: {\n                            column: {\n                                field: \"column_field\"\n                            }\n                        }\n                    });\n                    var expected_val = {\n                        column_field: {\n                            formula: \"expected formula\",\n                            formatted_value: \"\"\n                        }\n                    };\n                    target.cell_editor.loadValue(expected_val);\n                    assertDeepAreSame(\n                        { formula: 'expected formula' },\n                        target.cell_editor.serializeValue(),\n                        \"serializeValue did not return val\"\n                    );\n                },\n\n                testApplyValueDoes : function () {\n                    var target = create_div_and_editor_for({\n                        test_name: \"testApplyValueDoes\",\n                        editor_args: {\n                            column: {\n                                field: \"column_field\"\n                            }\n                        }\n                    });\n                    var expected_item = {\n                        column_field: {\n                            formula: \"new formula\",\n                            formatted_value: \"new value\"\n                        }\n                    };\n                    var item = {};\n                    target.cell_editor.applyValue(item, {formula: 'new formula', formatted_value: 'new value'});\n                    assertDeepAreSame(expected_item, item, \"applyValue didn't\");\n                },\n\n                testIsValueChanged : function () {\n                    var target = create_div_and_editor_for({\n                        test_name: \"testIsValueChanged\",\n                        editor_args: {\n                            column: {\n                                field: \"column_field\"\n                            }\n                        }\n                    });\n                    target.cell_editor.loadValue({\n                        column_field: {\n                            formula: \"expected formula\",\n                            formatted_value: \"\"\n                        }\n                    });\n\n                    YAHOO.util.Assert.isFalse(target.cell_editor.isValueChanged(), 'isValueChanged incorrect');\n                    target.cell_editor.$input.val('something');\n                    YAHOO.util.Assert.isTrue(target.cell_editor.isValueChanged(), 'isValueChanged incorrect');\n                },\n\n                testValidate: function () {\n                    var target = create_div_and_editor_for({\n                        test_name: \"testValidate\",\n                        editor_args: {\n                            column: {\n                                field: \"column_field\"\n                            }\n                        }\n                    });\n                    assertDeepAreSame({valid: true, msg: null}, target.cell_editor.validate());\n                },\n\n\n                testEditorUpdatesAssociatedFormulaBarOnImportantEvents : function() {\n                    var target = create_div_and_editor_for({\n                        test_name: \"testEditorUpdatesAssociatedFormulaBarOnImportantEvents\",\n                        editor_args: {\n                            column: {\n                                field: \"column_field\"\n                            }\n                        }\n                    });\n\n                    // Firefox and Chrome\n                    YAHOO.util.Assert.areSame('', target.formula_bar.val());\n                    target.cell_editor.$input.val('s');\n                    target.cell_editor.$input.trigger('input');\n                    YAHOO.util.Assert.areSame('s', target.formula_bar.val());\n\n                    // Internet Explorer\n                    target.cell_editor.$input.val('u');\n                    target.cell_editor.$input.trigger('propertychange');\n                    YAHOO.util.Assert.areSame('u', target.formula_bar.val());\n                },\n\n\n                testFormulaBarUpdatesAssociatedEditorOnImportantEvents : function() {\n                    var target = create_div_and_editor_for({\n                        test_name: \"testFormulaBarUpdatesAssociatedEditorOnImportantEvents\",\n                        editor_args: {\n                            column: {\n                                field: \"column_field\"\n                            }\n                        }\n                    });\n\n                    // Firefox and Chrome\n                    YAHOO.util.Assert.areSame('', target.cell_editor.$input.val());\n                    target.formula_bar.val('s');\n                    target.formula_bar.trigger('input');\n                    YAHOO.util.Assert.areSame('s', target.cell_editor.$input.val());\n\n                    // Internet Explorer\n                    target.formula_bar.val('u');\n                    target.formula_bar.trigger('propertychange');\n                    YAHOO.util.Assert.areSame('u', target.cell_editor.$input.val());\n                },\n\n\n                testCellEditorUnbindsFromFormulaBarWhenDestroyed : function() {\n                    var target = create_div_and_editor_for({\n                        test_name: \"testFormulaBarUpdatesAssociatedEditorOnImportantEvents\",\n                        editor_args: {\n                            column: {\n                                field: \"column_field\"\n                            }\n                        }\n                    });\n\n                    var unbindArgs = [];\n                    target.formula_bar.unbind = function(eventType, handler) {\n                        unbindArgs.push([eventType, handler]);\n                    };\n                    target.cell_editor.destroy();\n\n                    assertDeepAreSame(\n                        [\n                            [\"input\", target.cell_editor.updateInput],\n                            [\"propertychange\", target.cell_editor.updateInput],\n                            [\"keydown\", target.cell_editor.forwardReturnsToEditor]\n                        ],\n                        unbindArgs,\n                        'events not unbound on formula bar destroy'\n                    );\n                },\n\n\n                testEnterKeyDownInFormulaBarForwardedToEditorInput : function() {\n                    var target = create_div_and_editor_for({\n                        test_name: \"testEnterKeyDownInFormulaBarForwardedToEditorInput\",\n                        editor_args: {\n                            column: {\n                                field: \"column_field\"\n                            }\n                        }\n                    });\n\n                    var keyDownFiredOnEditor = false;\n                    target.cell_editor.$input.bind(\n                        \"keydown\",\n                        function() {\n                            keyDownFiredOnEditor = true;\n                        }\n                    );\n\n                    var evt = $.Event(\"keydown\");\n\n                    evt.which = 65;\n                    keyDownFiredOnEditor = false;\n                    target.formula_bar.trigger(evt);\n                    YAHOO.util.Assert.isFalse(keyDownFiredOnEditor, 'Editor keydown unexpectedly fired');\n\n                    evt.which = 13;\n                    keyDownFiredOnEditor = false;\n                    target.formula_bar.trigger(evt);\n                    YAHOO.util.Assert.isTrue(keyDownFiredOnEditor, 'Editor keydown not fired');\n\n                    target.cell_editor.destroy();\n                    evt.which = 13;\n                    keyDownFiredOnEditor = false;\n                    target.formula_bar.trigger(evt);\n                    YAHOO.util.Assert.isFalse(keyDownFiredOnEditor, 'Editor keydown unexpectedly fired after destruction');\n                }\n            })\n\n        ];\n    </script>\n    <script src=\"yuirunner.js\"></script>\n\n    <div id=\"test-divs\"></div>\n\n  </body>\n\n</html>\n\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/tests/console_view_test.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n<html>\n<head>\n    <script type=\"text/javascript\" src=\"/static/jquery/jquery-1.4.2.min.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/jquery/jquery-ui-1.8.10.custom.min.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/dirigible/tests/test_utils.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/json/json2.js\"></script>\n    <script src=\"yuitest/yuitest-combo.js\"></script>\n    <script src=\"jsmock.js\"></script>\n\n    <script src=\"/static/dirigible/scripts/console_view.js\"></script>\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"logger.css\" />\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"testlogger.css\" />\n</head>\n\n<body>\n<div id=\"id_results\">Please wait</div>\n\n\n<script type=\"text/javascript\">\n\n    tests = [\n\n        new YAHOO.tool.TestCase({\n\n            name: \"testConsoleView\",\n\n            setUp: function() {\n                this.mockControl = new MockControl();\n                this.consoleDiv = this.mockControl.createMock({\n                    html: function() {}\n                });\n                this.consoleView = new Dirigible.ConsoleView(this.consoleDiv);\n            },\n\n            testUpdateMetaDataWithNoTextShouldClear: function () {\n                this.consoleDiv.expects().html('');\n                this.consoleView.updateMetaData({});\n                this.mockControl.verify();\n            },\n\n            testUpdateMetaDataWithTextShouldShowText: function () {\n                this.consoleDiv.expects().html('some testy text');\n                this.consoleView.updateMetaData({'console_text': 'some testy text'});\n                this.mockControl.verify();\n            }\n        })\n\n    ];\n</script>\n\n\n<script src=\"yuirunner.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/tests/dialogs_test.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n<html>\n<head>\n    <script type=\"text/javascript\" src=\"/static/jquery/jquery-1.4.2.min.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/jquery/jquery-ui-1.8.10.custom.min.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/dirigible/tests/test_utils.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/json/json2.js\"></script>\n    <script src=\"yuitest/yuitest-combo.js\"></script>\n    <script src=\"jsmock.js\"></script>\n\n    <script src=\"/static/dirigible/scripts/dialogs.js\"></script>\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"logger.css\" />\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"testlogger.css\" />\n</head>\n\n<body>\n<div id=\"id_results\">Please wait</div>\n\n<img id=\"id_import_button\" />\n<img id=\"id_export_button\" />\n<div id='id_import_form' style='display:none'>\n<form>\n    <input id=\"id_import_form_file\" />\n    <input id=\"id_import_form_column\" />\n    <input id=\"id_import_form_row\" />\n    <span class=\"import_form_radio_button\">\n        <input type=\"radio\" name=\"csv_encoding\" value=\"excel\" checked />\n    </span>\n    <span class=\"import_form_radio_button\">\n        <input type=\"radio\" name=\"csv_encoding\" value=\"other\" />\n    </span>\n    <input id=\"id_import_form_upload_csv_button\" />\n    <input id=\"id_import_form_upload_csv_button\" />\n    <input id=\"id_import_form_upload_xls_values_button\" />\n    <input id=\"id_import_form_upload_xls_formulae_button\" />\n    <input id=\"id_import_form_cancel_button\" />\n    </form>\n    </div>\n\n<div id=\"id_export_dialog\" style=\"display: none\">\n    <input id=\"id_export_dialog_close_button\" />\n</div>\n\n\n<script type=\"text/javascript\">\n\n    tests = [\n\n        new YAHOO.tool.TestCase({\n\n            name: \"testImportInitialise\",\n\n            setUp : function () {\n                this.mockControl = new MockControl();\n                this.mockGrid = this.mockControl.createMock({\n                    getActiveCell: function () {}\n                });\n            },\n\n            testInitialiseImport: function () {\n                var mockCell = {cell: 12, row: 13};\n                this.mockGrid.expects().getActiveCell().andReturn(mockCell);\n\n                Dirigible.Dialogs.Initialise(this.mockGrid, {});\n\n                $('#id_import_form_file').val('previous_file_value.xls');\n\n                $('#id_import_button').trigger('click');\n\n                YAHOO.util.Assert.areSame(\n                    \"\" + mockCell.cell,\n                    $('#id_import_form_column').val()\n                );\n                YAHOO.util.Assert.areSame(\n                    \"\" + (mockCell.row + 1),\n                    $('#id_import_form_row').val()\n                );\n                elementVisible($('#id_import_form'), true);\n                elementVisible($('span.import_form_radio_button'), false);\n                elementVisible($('#id_import_form_upload_csv_button'), false);\n                elementVisible(\n                    $('#id_import_form_upload_xls_values_button'), false\n                );\n                YAHOO.util.Assert.areSame(\n                    \"\",\n                    $('#id_import_form_file').val()\n                );\n\n                this.mockControl.verify();\n            },\n\n            testInitialiseExport: function () {\n                Dirigible.Dialogs.Initialise(this.mockGrid, {});\n\n                $('#id_export_button').trigger('click');\n\n                elementVisible($('#id_export_dialog'), true);\n            },\n\n            testImportCancelButton: function() {\n                Dirigible.Dialogs.Initialise(this.mockGrid, {});\n                $('#id_import_button').trigger('click');\n\n                $('#id_import_form_cancel_button').trigger('click');\n\n                elementVisible($('#id_import_form'), false);\n            },\n\n            testExportCancelButton: function() {\n                Dirigible.Dialogs.Initialise(this.mockGrid, {});\n                $('#id_export_button').trigger('click');\n\n                $('#id_export_dialog_close_button').trigger('click');\n\n                elementVisible($('#id_export_dialog'), false);\n            },\n\n            testFileFieldChangeShowsUploadCSVButtonAndRadioButtonsForCSVFile: function() {\n                Dirigible.Dialogs.Initialise(\n                    this.mockGrid,\n                    {\n                        importXls: 'xl_url',\n                        importCsv: 'csv_url'\n                    }\n                );\n                $('#id_import_form form').attr('action', 'initial value');\n                $('#id_import_button').trigger('click');\n                elementVisible($('span.import_form_radio_button'), false);\n                elementVisible($('#id_import_form_upload_csv_button'), false);\n                elementVisible($('#id_import_form_upload_xls_values_button'), false);\n                $('#id_import_form_file').val('myfile.csv');\n                $('#id_import_form_file').change();\n                elementVisible($('span.import_form_radio_button'), true);\n                elementVisible($('#id_import_form_upload_csv_button'), true);\n                elementVisible($('#id_import_form_upload_xls_values_button'), false);\n                YAHOO.util.Assert.areSame(\n                        'csv_url',\n                        $('#id_import_form form').attr('action')\n                );\n\n            },\n\n            testFileFieldChangeShowsUploadCSVButtonAndRadioButtonsForOtherFiles: function() {\n                Dirigible.Dialogs.Initialise(\n                    this.mockGrid,\n                    {\n                        importXls: 'xl_url',\n                        importCsv: 'csv_url'\n                    }\n                );\n                $('#id_import_button').trigger('click');\n                elementVisible($('span.import_form_radio_button'), false);\n                elementVisible($('#id_import_form_upload_csv_button'), false);\n                elementVisible($('#id_import_form_upload_xls_values_button'), false);\n                $('#id_import_form_file').val('myfile.other');\n                $('#id_import_form_file').change();\n                elementVisible($('span.import_form_radio_button'), true);\n                elementVisible($('#id_import_form_upload_csv_button'), true);\n                elementVisible($('#id_import_form_upload_xls_values_button'), false);\n                YAHOO.util.Assert.areSame(\n                        'csv_url',\n                        $('#id_import_form form').attr('action')\n                );\n            },\n\n            testFileFieldChangeShowsOnlyUploadXlsButtonForXlsFile: function() {\n                Dirigible.Dialogs.Initialise(\n                    this.mockGrid,\n                    {\n                        importXls: 'xl_url',\n                        importCsv: 'csv_url'\n                    }\n                );\n                $('#id_import_button').trigger('click');\n                elementVisible($('#id_import_form_upload_csv_button'), false);\n                elementVisible($('span.import_form_radio_button'), false);\n                elementVisible($('#id_import_form_upload_xls_values_button'), false);\n\n                $('#id_import_form span.import_form_radio_button').show();\n\n                $('#id_import_form_file').val('myfile.xls');\n                $('#id_import_form_file').trigger('change');\n\n                elementVisible($('span.import_form_radio_button'), false);\n                elementVisible($('#id_import_form_upload_csv_button'), false);\n                elementVisible($('#id_import_form_upload_xls_values_button'), true);\n                YAHOO.util.Assert.areSame(\n                        'xl_url',\n                        $('#id_import_form form').attr('action')\n                );\n            }\n\n        })\n\n    ];\n</script>\n\n\n<script src=\"yuirunner.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/tests/editor_commands_test.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n<html>\n<head>\n    <script type=\"text/javascript\" src=\"/static/jquery/jquery-1.4.2.min.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/dirigible/tests/test_utils.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/json/json2.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/slickgrid/slick.core.js\"></script>\n    <script type=\"text/javascript\" src=\"yuitest/yuitest-combo.js\"></script>\n    <script type=\"text/javascript\" src=\"jsmock.js\"></script>\n\n    <script type=\"text/javascript\" src=\"../scripts/editor_commands.js\"></script>\n\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"logger.css\" />\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"testlogger.css\" />\n</head>\n\n<body>\n<div id=\"id_results\">Please wait</div>\n\n\n<script type=\"text/javascript\">\n\ntests = [\n\n    new YAHOO.tool.TestCase({\n\n        name: \"testEditorCommands\",\n\n        testSaveUsercodeSavesUsercodeWhenEditorDirtyAndClearsFlag: function () {\n            var mockControl = new MockControl();\n            Dirigible.SheetUtils = mockControl.createMock( {\n                abortOtherRecalculations: function () {},\n                queueRecalculation: function () {}\n            } );\n            Dirigible.SheetUtils.expects().abortOtherRecalculations();\n\n            var mockEditor = mockControl.createMock( { getSession: function() {} } );\n            var mockSession = mockControl.createMock( { getValue: function(){} } );\n            mockEditor.expects().getSession().andReturn( mockSession );\n            mockSession.expects().getValue().andReturn( 'some usercode' );\n\n            var post_url = null;\n            var post_params = null;\n            var post_callback = null;\n            var urls = {\n                setSheetUsercode: \"set_usercode_url\"\n            };\n            var commands = new Dirigible.EditorCommands(urls, mockEditor);\n            $.post = function(url, params, callback) {\n                post_url = url;\n                post_params = params;\n                post_callback = callback;\n            };\n\n            mockEditor.usercodeDirty = true;\n            commands.saveUsercode();\n\n            mockControl.verify();\n            YAHOO.util.Assert.areSame(urls.setSheetUsercode, post_url);\n            assertDeepAreSame({ 'usercode': 'some usercode' }, post_params);\n            YAHOO.util.Assert.areSame(Dirigible.SheetUtils.queueRecalculation, post_callback);\n            YAHOO.util.Assert.isFalse(mockEditor.usercodeDirty);\n        },\n\n        testSaveUsercodeDoesntSaveIfEditorNotDirty: function () {\n            var mockControl = new MockControl();\n            Dirigible.SheetUtils = mockControl.createMock({});\n\n            var mockEditor = mockControl.createMock({});\n\n            var urls = {};\n            var commands = new Dirigible.EditorCommands(urls, mockEditor);\n            mockEditor.usercodeDirty = false;\n            commands.saveUsercode();\n\n            mockControl.verify();\n            YAHOO.util.Assert.isFalse(mockEditor.usercodeDirty);\n        }\n\n\n    })\n\n];\n</script>\n\n\n<script src=\"yuirunner.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/tests/feedback_dialog_test.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n<html>\n<head>\n    <script type=\"text/javascript\" src=\"/static/jquery/jquery-1.4.2.min.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/jquery/jquery-ui-1.8.10.custom.min.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/dirigible/tests/test_utils.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/json/json2.js\"></script>\n    <script src=\"yuitest/yuitest-combo.js\"></script>\n    <script src=\"jsmock.js\"></script>\n\n    <script src=\"/static/dirigible/scripts/feedback_dialog.js\"></script>\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"logger.css\" />\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"testlogger.css\" />\n</head>\n\n<body>\n<div id=\"id_results\">Please wait</div>\n\n<a href=\"\" id=\"make_me_a_feedback\" class=\"feedback_link\" >feedback link</a>\n\n<div id=\"id_feedback_dialog\" style=\"display:none\">\n    <input type=\"button\" id=\"id_feedback_dialog_ok_button\" />\n    <input type=\"button\" id=\"id_feedback_dialog_cancel_button\" />\n    <textarea id=\"id_feedback_dialog_text\"></textarea>\n    <input type=\"text\" id=\"id_feedback_dialog_email_address\" />\n    <div id=\"id_feedback_dialog_error\"></div>\n    <div id=\"id_feedback_dialog_spinner\"></div>\n</div>\n\n\n<script type=\"text/javascript\">\n\n    tests = [\n\n        new YAHOO.tool.TestCase({\n\n            name: \"testFeedbackDialog\",\n\n            setUp: function() {\n                this.urls = {feedback: 'feedback url'};\n            },\n\n            tearDown: function () {\n                $('#id_feedback_dialog').hide();\n            },\n\n            testClickingFeedbackLinkShouldOpenAndHideErrorAndSpinnerAndEnableButtons: function () {\n                Dirigible.FeedbackDialog.Initialise(this.urls);\n\n                // Sanity check\n                elementVisible($('#id_feedback_dialog'), false, 'initial dialog visibility wrong');\n\n                // setup dialog visibility and button enabled so we can assert that\n                // showing the form assigns to them\n                $('#id_feedback_dialog_error').removeClass('hidden');\n                $('#id_feedback_dialog_spinner').removeClass('hidden');\n                $('#id_feedback_dialog_ok_button').attr('disabled', 'disabled');\n                $('#id_feedback_dialog_cancel_button').attr('disabled', 'disabled');\n\n                var event = new jQuery.Event('click');\n                $('#make_me_a_feedback').trigger(event);\n\n                elementVisible($('#id_feedback_dialog'), true, 'dialog did not appear');\n                YAHOO.util.Assert.isTrue(event.isDefaultPrevented());\n                YAHOO.util.Assert.areSame(600, $('#id_feedback_dialog').parent().width());\n                YAHOO.util.Assert.areSame(0, $('.ui-widget-overlay').length, 'dialog should not be modal');\n                YAHOO.util.Assert.isTrue($('#id_feedback_dialog_error').hasClass('hidden'), 'ajax error visible');\n                YAHOO.util.Assert.isTrue($('#id_feedback_dialog_spinner').hasClass('hidden'), 'spinner visible');\n\n                YAHOO.util.Assert.isFalse(\n                    $('#id_feedback_dialog_ok_button').attr('disabled'),\n                    'ok should be enabled'\n                );\n                YAHOO.util.Assert.isFalse(\n                    $('#id_feedback_dialog_cancel_button').attr('disabled'),\n                    'cancel should be enabled'\n                );\n            },\n\n            testClickingOKShouldHideErrorMessageShowSpinnerAndSubmitWithCallbacks: function () {\n                Dirigible.FeedbackDialog.Initialise(this.urls, 'user name');\n                $('#make_me_a_feedback').trigger('click');\n                $(\"#id_feedback_dialog_text\").val('some text');\n                $(\"#id_feedback_dialog_email_address\").val('user email');\n                $('#id_feedback_dialog_error').removeClass('hidden')\n                $('#id_feedback_dialog_spinner').addClass('hidden')\n\n                var ajaxParams;\n                function mockAjax(params) {\n                    ajaxParams = params;\n                }\n                var originalAjax = $.ajax;\n                $.ajax = mockAjax;\n                try {\n                    $('#id_feedback_dialog_ok_button').trigger('click');\n                } finally {\n                    $.ajax = originalAjax;\n                }\n                YAHOO.util.Assert.areSame('POST', ajaxParams.type, 'wrong method');\n                YAHOO.util.Assert.areSame(this.urls.feedback, ajaxParams.url, 'wrong url');\n                YAHOO.util.Assert.areSame('some text', ajaxParams.data.message, 'wrong message');\n                YAHOO.util.Assert.areSame('user email', ajaxParams.data.email_address, 'wrong email address');\n                YAHOO.util.Assert.areSame('user name', ajaxParams.data.username, 'wrong username');\n\n                YAHOO.util.Assert.isTrue(\n                    $('#id_feedback_dialog_ok_button').attr('disabled'),\n                    'ok not disabled'\n                );\n                YAHOO.util.Assert.isTrue(\n                    $('#id_feedback_dialog_cancel_button').attr('disabled'),\n                    'cancel not disabled'\n                );\n                YAHOO.util.Assert.isTrue($('#id_feedback_dialog_error').hasClass('hidden'), 'ajax error visible too soon');\n                YAHOO.util.Assert.isFalse($('#id_feedback_dialog_spinner').hasClass('hidden'), 'spinner not visible');\n\n                ajaxParams.error();\n                elementVisible($('#id_feedback_dialog'), true, 'ajax error closed dialog');\n                YAHOO.util.Assert.isFalse($('#id_feedback_dialog_error').hasClass('hidden'), 'ajax error not visible');\n                YAHOO.util.Assert.isTrue($('#id_feedback_dialog_spinner').hasClass('hidden'), 'spinner visible on error');\n                YAHOO.util.Assert.isFalse(\n                    $('#id_feedback_dialog_ok_button').attr('disabled'),\n                    'ok should be re-enabled'\n                );\n                YAHOO.util.Assert.isFalse(\n                    $('#id_feedback_dialog_cancel_button').attr('disabled'),\n                    'cancel should be re-enabled'\n                );\n\n                ajaxParams.success();\n                elementVisible($('#id_feedback_dialog'), false, 'ajax success did not close dialog');\n            },\n\n            testClickingCancelShouldClose: function () {\n                Dirigible.FeedbackDialog.Initialise(this.urls);\n                $('#make_me_a_feedback').trigger('click');\n                $('#id_feedback_dialog_cancel_button').trigger('click');\n\n                elementVisible($('#id_feedback_dialog'), false, 'cancel did not close dialog');\n            },\n\n            testEmailAddressPromptShouldHaveCorrectDefaultsAndBehaviourWhenUsernameIsEmpty: function () {\n                Dirigible.FeedbackDialog.Initialise(this.urls, '');\n                $('#make_me_a_feedback').trigger('click');\n\n                YAHOO.util.Assert.areSame(\n                    \"Email address (optional - only necessary if you would like us to contact you)\",\n                    $('#id_feedback_dialog_email_address').val()\n                );\n                YAHOO.util.Assert.isTrue(\n                    $('#id_feedback_dialog_email_address').hasClass(\"email_prompt\"),\n                    'before click email address has wrong class'\n                );\n\n                $('#id_feedback_dialog_email_address').trigger('focus');\n                YAHOO.util.Assert.areSame(\n                    \"\",\n                    $('#id_feedback_dialog_email_address').val(),\n                    'email address prompt not cleared on click'\n                );\n                YAHOO.util.Assert.isFalse(\n                    $('#id_feedback_dialog_email_address').hasClass(\"email_prompt\"),\n                    'after click email address has wrong class'\n                );\n\n                $('#id_feedback_dialog_email_address').val(\"Harold@here.com\");\n                $('#id_feedback_dialog_email_address').trigger('click');\n                YAHOO.util.Assert.areSame(\n                    \"Harold@here.com\",\n                    $('#id_feedback_dialog_email_address').val(),\n                    'changed email address prompt not maintained on click'\n                );\n                YAHOO.util.Assert.isFalse(\n                    $('#id_feedback_dialog_email_address').hasClass(\"email_prompt\"),\n                    'after second click email address has wrong class'\n                );\n\n                $('#id_feedback_dialog').dialog('close');\n                $('#make_me_a_feedback').trigger('click');\n                YAHOO.util.Assert.areSame(\n                    \"Harold@here.com\",\n                    $('#id_feedback_dialog_email_address').val(),\n                    'changed email address prompt not maintained close and re-open'\n                );\n                YAHOO.util.Assert.isFalse(\n                    $('#id_feedback_dialog_email_address').hasClass(\"email_prompt\"),\n                    'after close and re-open email address has wrong class'\n                );\n\n            },\n\n            testEmailAddressPromptShouldBeInvisibleWhenUsernameIsNonEmpty: function () {\n                Dirigible.FeedbackDialog.Initialise(this.urls, 'a user name');\n                $('#make_me_a_feedback').trigger('click');\n\n                elementVisible($('#id_feedback_dialog'), true, 'dialog not visible');\n                elementVisible($('#id_feedback_dialog_email_address'), false, 'email address not hidden');\n            }\n\n        })\n\n    ];\n</script>\n\n\n<script src=\"yuirunner.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/tests/grid_commands_test.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n<html>\n<head>\n    <script type=\"text/javascript\" src=\"/static/jquery/jquery-1.4.2.min.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/dirigible/tests/test_utils.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/json/json2.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/slickgrid/slick.core.js\"></script>\n    <script type=\"text/javascript\" src=\"yuitest/yuitest-combo.js\"></script>\n    <script type=\"text/javascript\" src=\"jsmock.js\"></script>\n\n    <script type=\"text/javascript\" src=\"../scripts/grid_commands.js\"></script>\n\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"logger.css\" />\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"testlogger.css\" />\n</head>\n\n<body>\n    <div id=\"id_results\">Please wait</div>\n\n<script type=\"text/javascript\">\n\ntests = [\n\n    new YAHOO.tool.TestCase({\n\n        name: \"testCreateSelectionCommand\",\n\n        testCreateSelectionCommand_WhenNotEditing_PerformsServerCommandAndReturnsTrue: function () {\n            Dirigible.SheetUtils = {\n                queueRecalculation: \"a mock recalculation function\"\n            };\n            var selectedRange = new Slick.Range(1, 3, 5, 7);\n            var mockControl = new MockControl();\n            var mockSelectionModel = mockControl.createMock({\n                getSelectedRanges: function() {}\n            });\n            var mockGrid = mockControl.createMock({\n                getCellEditor: function() {},\n                getSelectionModel: function() {}\n            });\n            var sentStartCol, sentStartRow, sentEndCol, sentEndRow;\n            var serverCommand = function(startCol, startRow, endCol, endRow) {\n                sentStartCol = startCol;\n                sentStartRow = startRow;\n                sentEndCol = endCol;\n                sentEndRow = endRow;\n            };\n\n            mockGrid.expects().getCellEditor().andReturn(null);\n            mockGrid.expects().getSelectionModel().andReturn(mockSelectionModel);\n            mockSelectionModel.expects().getSelectedRanges().andReturn([selectedRange]);\n\n            commands = new Dirigible.GridCommands(null, mockGrid);\n            var gridSelectionAjaxAction = Dirigible.GridCommands.createSelectionCommand__(\n                mockGrid, serverCommand\n            );\n            var result = gridSelectionAjaxAction();\n\n            mockControl.verify();\n            YAHOO.util.Assert.areSame(3, sentStartCol);\n            YAHOO.util.Assert.areSame(2, sentStartRow);\n            YAHOO.util.Assert.areSame(7, sentEndCol);\n            YAHOO.util.Assert.areSame(6, sentEndRow);\n            YAHOO.util.Assert.isTrue(result);\n        },\n\n        testCreateSelectionCommand_WhenEditing_DoesNothingAndReturnsFalse: function (modifier) {\n            Dirigible.SheetUtils = {\n                queueRecalculation: \"a mock recalculation function\"\n            };\n            var mockControl = new MockControl();\n            var mockSelectionModel = mockControl.createMock({\n                getSelectedRanges: function() {}\n            });\n            var mockGrid = mockControl.createMock({\n                getSelectionModel: function() {},\n                getCellEditor: function() {}\n            });\n            var serverCommandCalled = false;\n            var serverCommand = function() {\n                serverCommandCalled = true;\n            };\n\n            mockGrid.expects().getCellEditor().andReturn(true);\n\n            commands = new Dirigible.GridCommands(null, mockGrid);\n            var gridSelectionAjaxAction = Dirigible.GridCommands.createSelectionCommand__(\n                mockGrid, serverCommand\n            );\n            var result = gridSelectionAjaxAction();\n\n            mockControl.verify();\n            YAHOO.util.Assert.isFalse(serverCommandCalled);\n            YAHOO.util.Assert.isFalse(result);\n        }\n\n    }),\n\n\n    new YAHOO.tool.TestCase({\n\n        name: \"testClipboardAjaxMethods\",\n\n        setUp : function () {\n            this.post_url = null;\n            this.post_params = null;\n            this.post_callback = null;\n            this.urls = {\n                cut: 'cut_url',\n                copy: 'copy_url',\n                paste: 'paste_url'\n            };\n            Dirigible.SheetUtils = {\n                queueRecalculation: \"a mock recalculation function\"\n            };\n            this.commands = new Dirigible.GridCommands(this.urls, null);\n            var that = this;\n            $.post = function(url, params, callback) {\n                that.post_url = url;\n                that.post_params = params;\n                that.post_callback = callback;\n            };\n        },\n\n        testSendCopyToServer: function () {\n            this.commands.sendCopyToServer__(1, 2, 3, 4);\n            YAHOO.util.Assert.areSame(this.urls.copy, this.post_url);\n            assertDeepAreSame({range: '1,2,3,4'}, this.post_params);\n        },\n\n        testSendCutToServer: function () {\n            this.commands.sendCutToServer__(1, 2, 3, 4);\n            YAHOO.util.Assert.areSame(this.urls.cut, this.post_url);\n            assertDeepAreSame({range: '1,2,3,4'}, this.post_params);\n            YAHOO.util.Assert.areSame(\n                Dirigible.SheetUtils.queueRecalculation,\n                this.post_callback\n            );\n        },\n\n        testSendPasteToServer: function () {\n            this.commands.sendPasteToServer__(1, 2, 3, 4);\n            YAHOO.util.Assert.areSame(this.urls.paste, this.post_url);\n            assertDeepAreSame({range: '1,2,3,4'}, this.post_params);\n            YAHOO.util.Assert.areSame(\n                Dirigible.SheetUtils.queueRecalculation,\n                this.post_callback\n            );\n        }\n\n    }),\n\n\n    new YAHOO.tool.TestCase({\n\n        name: \"testAjaxClearMethod\",\n\n        setUp : function () {\n            this.post_url = null;\n            this.post_params = null;\n            this.post_callback = null;\n            this.urls = {\n                clearCells: 'clear_cells_url'\n            };\n            var callbacks = {\n                    update_grid_and_show_errors : 'update_grid_and_show_errors'\n            };\n            this.commands = new Dirigible.GridCommands(this.urls, null);\n            var that = this;\n            $.post = function(url, params, callback) {\n                that.post_url = url;\n                that.post_params = params;\n                that.post_callback = callback;\n            };\n        },\n\n        testSendClearToServer: function () {\n            this.commands.sendClearToServer__(1, 2, 3, 4);\n            YAHOO.util.Assert.areSame(this.urls.clearCells, this.post_url);\n            assertDeepAreSame({range: '1,2,3,4'}, this.post_params);\n            YAHOO.util.Assert.areSame(\n                Dirigible.SheetUtils.queueRecalculation,\n                this.post_callback\n            );\n        }\n\n    }),\n\n\n    new YAHOO.tool.TestCase({\n\n        name: \"testClipboardActions\",\n\n        testCutCopyPasteAndClear_AreGridSelectionAjaxActions_Calling_RelevantAjaxActions: function () {\n            var mockControl = new MockControl();\n            var mockGrid = mockControl.createMock({});\n\n            var orig = Dirigible.GridCommands.createSelectionCommand__;\n            try {\n                Dirigible.GridCommands.createSelectionCommand__ = function(grid, ajaxAction) {\n                    YAHOO.util.Assert.areSame(grid, mockGrid);\n                    return { \"wrapped\": ajaxAction };\n                };\n\n                var commands = new Dirigible.GridCommands(null, mockGrid);\n                YAHOO.util.Assert.areSame(\n                    commands.sendCutToServer__,\n                    commands.cut.wrapped,\n                    'bad cut'\n                );\n                YAHOO.util.Assert.areSame(\n                    commands.sendCopyToServer__,\n                    commands.copy.wrapped,\n                    'bad copy'\n                );\n                YAHOO.util.Assert.areSame(\n                    commands.sendPasteToServer__,\n                    commands.paste.wrapped,\n                    'bad paste'\n                );\n                YAHOO.util.Assert.areSame(\n                    commands.sendClearToServer__,\n                    commands.clear.wrapped,\n                    'bad clear'\n                );\n\n            } finally {\n                Dirigible.GridCommands.createSelectionCommand__ = orig;\n            }\n        }\n\n    }),\n\n   new YAHOO.tool.TestCase({\n\n        name: \"testColumnResizing\",\n        setUp: function () {\n           this.gridColumns = [\n                {width: 100, previousWidth: 100},\n                {width: 101, previousWidth: 100},\n                {width: 100, previousWidth: 100},\n                {width: 200, previousWidth: 300}\n            \n            ];\n            this.expectedJson = '{\"1\":101,\"3\":200}';\n        },\n\n        testGridColumnsToJSON: function() {\n            var commands = new Dirigible.GridCommands(null, null);\n        \n            var json = commands.convertGridColumnsToJSON__(this.gridColumns);\n\n\n            assertDeepAreSame(this.expectedJson, json);\n        },\n\n        testColumnResizing: function() {\n            var mockControl = new MockControl();\n\n            var mockGrid = mockControl.createMock({\n                getColumns: function() {}\n            });\n            var mockJquery = mockControl.createMock({\n                post: function() {}\n            });\n            $.extend(true, $, mockJquery);\n\n            mockGrid.expects().getColumns().andReturn(this.gridColumns);\n            var postArgs\n\n            mockJquery.expects().post('url', TypeOf.isA(Object)).andStub(\n                function () {\n                    postArgs = arguments[1];\n                }\n            );\n\n            var urls = {setColumnWidths : 'url'};\n            var expectedPostArgs = { column_widths: this.expectedJson};\n\n            var commands = new Dirigible.GridCommands(urls, mockGrid);\n            var event;\n            var data;\n            \n            commands.resizeColumns(event, data);\n\n            mockControl.verify();\n            assertDeepAreSame(expectedPostArgs, postArgs);\n        }\n    }),\n\n\n    new YAHOO.tool.TestCase({\n\n        name: \"testSendCellChangeToServer\",\n\n        testSendCellChangeToServer: function() {\n            var mockControl = new MockControl();\n            var mockJquery = mockControl.createMock({\n                post: function() {}\n            });\n            $.extend(true, $, mockJquery);\n\n            var post_url, post_params, post_callback;\n            var urls = {setCellFormula: 'set cell formula url'};\n\n            Dirigible.SheetUtils = mockControl.createMock({\n                abortOtherRecalculations: function () {}\n            });\n\n            Dirigible.SheetUtils.expects().abortOtherRecalculations();\n            mockJquery.expects().post(\n                'set cell formula url', \n                TypeOf.isA(Array),\n                TypeOf.isA(Function)\n            ).andStub(\n                function(url, params, callback) {\n                    post_url = url;\n                    post_params = params;\n                    post_callback = callback;\n                }\n            );\n            var commands = new Dirigible.GridCommands(urls, 'mockGrid');\n            slickGridCellDataToPostParams = function(grid, gridLocation) {\n                return [grid, gridLocation];\n            };\n\n            commands.sendCellChangeToServer(null, 'gridLocation');\n\n            mockControl.verify();\n\n            assertDeepAreSame(\n                post_params,  ['mockGrid', 'gridLocation']\n            );\n\n            // test post callback\n            var setCellFormulaAndUpdateSheetCallCount = 0;\n            var setCellFormulaAndUpdateSheetLocation;\n            commands.setCellFormulaAndUpdateSheet__ = function(gridLocation) {\n                setCellFormulaAndUpdateSheetCallCount++;\n                setCellFormulaAndUpdateSheetLocation = gridLocation;\n            };\n            post_callback('OK');\n            YAHOO.util.Assert.areSame(\n                1, setCellFormulaAndUpdateSheetCallCount\n            );\n            YAHOO.util.Assert.areSame(\n                'gridLocation',\n                setCellFormulaAndUpdateSheetLocation\n            );\n        },\n\n        testSetCellFormulaAndUpateSheet: function() {\n            var mockControl = new MockControl();\n\n            var mockGrid = mockControl.createMock({\n                updateCell: function() {}\n            });\n            mockGrid.expects().updateCell(2, 3);\n\n            mockSheetUtils = mockControl.createMock({\n                queueRecalculation: function() {}\n            });\n            mockSheetUtils.expects().queueRecalculation();\n            Dirigible.SheetUtils = mockSheetUtils;\n\n            var commands = new Dirigible.GridCommands(null, mockGrid);\n            commands.setCellFormulaAndUpdateSheet__( {row:2, column:3} );\n\n            mockControl.verify();\n        }\n\n    }),\n    new YAHOO.tool.TestCase({\n\n        name: \"testCommitCurrentEdit\",\n\n        setUp: function() {\n            this.mockControl = new MockControl();\n            this.mockEditController = this.mockControl.createMock({\n                commitCurrentEdit: function() {}\n            });\n            this.mockGrid = this.mockControl.createMock({\n                getEditController: function() {}\n            });\n        },\n\n        testCommitCurrentEditShouldCommitEditOnGrid: function() {\n            var urls = {};\n            var commands = new Dirigible.GridCommands(urls, this.mockGrid);\n\n            this.mockControl.reset();\n\n            this.mockGrid.expects().getEditController().andReturn(this.mockEditController);\n            this.mockEditController.expects().commitCurrentEdit();\n\n            commands.commitCurrentEdit();\n\n            this.mockControl.verify();\n        }\n    })\n];\n</script>\n\n\n<script src=\"yuirunner.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/tests/grid_content_converter_test.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n<html>\n  <head>\n      <script src=\"yuitest/yuitest-combo.js\"></script>\n      <script src=\"jsmock.js\"></script>\n\n      <script src=\"../../json/json2.js\"></script>\n      <script src=\"../../jquery/jquery-1.4.2.min.js\"></script>\n      <script src=\"../../slickgrid/slick.editors.js\"></script>\n\n      <script src=\"../scripts/grid_content_converter.js\"></script>\n\n      <script src=\"test_utils.js\"></script>\n      <link rel=\"stylesheet\" type=\"text/css\" href=\"logger.css\" />\n      <link rel=\"stylesheet\" type=\"text/css\" href=\"testlogger.css\" />\n\n\n  </head>\n\n  <body>\n    <div id=\"id_results\">Please wait</div>\n\n    <script type=\"text/javascript\">\n\n        tests = [\n\n            new YAHOO.tool.TestCase({\n\n                name: \"testSlickGridCellDataToPostParams\",\n\n                testConvertWithPopulatedCell : function () {\n                    var mockControl = new MockControl();\n                    var mockGrid = mockControl.createMock({\n                        getCellFormula: function() {}\n                    });\n\n                    var slickCellLocation = { row: 3, cell: 24 };\n                    mockGrid.expects().getCellFormula(slickCellLocation).andReturn(\n                        \"the changed formula\"\n                    );\n\n                    var postData = slickGridCellDataToPostParams(mockGrid, slickCellLocation);\n\n                    mockControl.verify();\n\n                    var expectedPostData = {\n                        formula: \"the changed formula\",\n                        column: 24,\n                        row: 4\n                    };\n                    assertDeepAreSame(expectedPostData, postData);\n                }\n\n            }),\n\n            new YAHOO.tool.TestCase({\n\n                name: \"testGetGridCellFormula\",\n\n                setUp: function() {\n                    this.mockControl = new MockControl();\n                    this.mockGrid = this.mockControl.createMock({\n                        getDataItem: function() {},\n                        getColumns: function() {}\n                    });\n                },\n\n                testGetGridCellFormulaShouldReturnFormulaIfThereIsOne: function () {\n                    var slickCellLocation = { row: 4, cell: 16 };\n\n                    this.mockGrid.expects().getColumns().andReturn(\n                        { 16: { field: \"column_field\" } }\n                    );\n                    this.mockGrid.expects().getDataItem(4).andReturn(\n                        { column_field: { formula: \"=A1\", formatted_value: \"22323\" } } \n                    );\n\n                    var result = getGridCellFormula(this.mockGrid, slickCellLocation);\n\n                    this.mockControl.verify();\n\n                    YAHOO.util.Assert.areSame(\"=A1\", result);\n                },\n\n                testGetGridCellFormulaShouldReturnEmptyStringIfGridModelHasNoDataForRow: function () {\n                    var slickCellLocation = { row: 4, cell: 16 };\n\n                    this.mockGrid.expects().getColumns().andReturn(\n                        { 16: { field: \"column_field\" } }\n                    );\n                    this.mockGrid.expects().getDataItem(4).andReturn(\n                        undefined\n                    );\n\n                    var result = getGridCellFormula(this.mockGrid, slickCellLocation);\n\n                    this.mockControl.verify();\n\n                    YAHOO.util.Assert.areSame(\"\", result);\n                },\n\n                testGetGridCellFormulaShouldReturnEmptyStringIfGridModelHasNoDataForColumnInThatRow: function () {\n                    var slickCellLocation = { row: 4, cell: 16 };\n\n                    this.mockGrid.expects().getColumns().andReturn(\n                        { 16: { field: \"column_field\" } }\n                    );\n                    this.mockGrid.expects().getDataItem(4).andReturn(\n                        { }\n                    );\n\n                    var result = getGridCellFormula(this.mockGrid, slickCellLocation);\n\n                    this.mockControl.verify();\n\n                    YAHOO.util.Assert.areSame(\"\", result);\n                },\n\n                testGetGridCellFormulaShouldReturnEmptyStringIfFormulaNotPresentInCell: function () {\n                    var slickCellLocation = { row: 4, cell: 16 };\n\n                    this.mockGrid.expects().getColumns().andReturn(\n                        { 16: { field: \"column_field\" } }\n                    );\n                    this.mockGrid.expects().getDataItem(4).andReturn(\n                        { column_field: { formatted_value: \"23232323\" } } \n                    );\n\n                    var result = getGridCellFormula(this.mockGrid, slickCellLocation);\n\n                    this.mockControl.verify();\n\n                    YAHOO.util.Assert.areSame(\"\", result);\n                }\n\n            })\n\n        ];\n    </script>\n    <script src=\"yuirunner.js\"></script>\n  </body>\n\n</html>\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/tests/grid_interaction_handler_test.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n<html>\n<head>\n    <script type=\"text/javascript\" src=\"/static/jquery/jquery-1.4.2.min.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/jquery/jquery.caret.1.02.min.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/dirigible/tests/test_utils.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/json/json2.js\"></script>\n    <script src=\"yuitest/yuitest-combo.js\"></script>\n    <script src=\"jsmock.js\"></script>\n\n    <script src=\"../scripts/grid_interaction_handler.js\"></script>\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"logger.css\" />\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"testlogger.css\" />\n</head>\n\n<body>\n    <div id=\"id_results\">Please wait</div>\n    <div id=\"id_editor_container\"></div>\n\n<script type=\"text/javascript\">\n\ntests = [\n\n    new YAHOO.tool.TestCase({\n\n        name: \"testConstructor\",\n\n        testBind_BindsHandlers: function () {\n            var mockJQuery = function(){ return {\n                keypress: function(){},\n                click: function(){}\n            } };\n            var mockControl = new MockControl();\n            var mockCommands = mockControl.createMock({\n                resizeColumns : function (){},\n                sendCellChangeToServer: function (){}\n            });\n            var mockGrid = mockControl.createMock({});\n            mockGrid.onKeyDown = mockControl.createMock({\n                subscribe: function(){}\n            });\n            mockGrid.onColumnsResized = mockControl.createMock({\n                subscribe: function(){}\n            });\n            mockGrid.onCellChange = mockControl.createMock({\n                subscribe: function(){}\n            });\n            mockGrid.onActiveCellChanged = mockControl.createMock({\n                subscribe: function(){}\n            });\n            mockGrid.onClick = mockControl.createMock({\n                subscribe: function(){}\n            });\n            mockGrid.onScroll = mockControl.createMock({\n                subscribe: function(){}\n            });\n\n            var gridInteractionHandler = new Dirigible.GridInteractionHandler(mockJQuery, mockGrid, mockCommands);\n\n            mockGrid.onKeyDown.expects().subscribe(gridInteractionHandler.handleKeyDown);\n            mockGrid.onColumnsResized.expects().subscribe(mockCommands.resizeColumns);\n            mockGrid.onCellChange.expects().subscribe(mockCommands.sendCellChangeToServer);\n            mockGrid.onActiveCellChanged.expects().subscribe(gridInteractionHandler.updateFormulaBar);\n            mockGrid.onClick.expects().subscribe(gridInteractionHandler.updateFormulaBar);\n            //mockGrid.onScroll.expects().subscribe(gridInteractionHandler.handleScroll);\n\n            gridInteractionHandler.bind();\n\n            mockControl.verify();\n        },\n\n        testBind_BindsDivKeyPressHandlerAndFormulaBarClickHandler: function () {\n            var mockControl = new MockControl();\n            var mockGrid = mockControl.createMock({});\n            mockGrid.onKeyDown = {\n                subscribe: function() {}\n            };\n            mockGrid.onColumnsResized = {\n                subscribe: function() {}\n            };\n            mockGrid.onCellChange = {\n                subscribe: function() {}\n            };\n            mockGrid.onActiveCellChanged = {\n                subscribe: function() {}\n            };\n            mockGrid.onClick = {\n                subscribe: function() {}\n            };\n            mockGrid.onScroll = {\n                subscribe: function(){}\n            };\n            var mockJQuery = mockControl.createMock({\n                find: function() {} \n            });\n            var mockDiv = mockControl.createMock({\n                keypress: function() {}\n            });\n            var mockFormulaBar = mockControl.createMock({\n                click: function() {}\n            });\n            \n            mockGridCommands = { resizeColumns: null };\n            var gridInteractionHandler = new Dirigible.GridInteractionHandler(mockJQuery.find, mockGrid, mockGridCommands);\n\n            mockJQuery.expects().find('#id_grid').andReturn(mockDiv);\n            mockDiv.expects().keypress(gridInteractionHandler.handleGridDivKeyPress);\n            mockJQuery.expects().find('#id_formula_bar').andReturn(mockFormulaBar);\n            mockFormulaBar.expects().click(gridInteractionHandler.handleFormulaBarClick);\n            \n            gridInteractionHandler.bind();\n\n            mockControl.verify();\n        }\n\n    }),\n\n    new YAHOO.tool.TestCase({\n\n        name: \"testHandleKeyDown\",\n\n        setUp: function() {\n            var mockJQuery = function(){ return {\n                keypress: function(){}               \n            } };\n            this.mockControl = new MockControl();\n            this.mockGrid = this.mockControl.createMock({\n                editActiveCell: function() {},\n                getActiveCell: function() {},\n                getColumns: function() {},\n                getDataLength: function() {},\n                getViewport: function() {},\n                gotoCell: function() {},\n                scrollRowIntoView: function() {},\n            });\n            this.mockGrid.onKeyDown = {\n                subscribe: function() {}\n            };\n            this.mockGrid.onColumnsResized = {\n                subscribe: function(){}\n            };\n            this.mockGrid.onCellChange = {\n                subscribe: function() {}\n            };\n            this.mockGrid.onActiveCellChanged = {\n                subscribe: function() {}\n            };\n            this.mockGrid.onClick = {\n                subscribe: function() {}\n            };\n            this.mockGrid.onScroll = {\n                subscribe: function(){}\n            };\n            this.mockGridCommands = this.mockControl.createMock({\n                clear: function() {},\n                copy: function() {},\n                cut: function() {},\n                paste: function() {}\n            });\n\n            this.gridInteractionHandler = new Dirigible.GridInteractionHandler(mockJQuery, this.mockGrid, this.mockGridCommands);  \n        },\n\n        testHandleKeyDown_ShouldSwallowEventsThatWouldMoveActiveCellOffValidGrid: function () {\n            var LEFT = 37;\n            var UP = 38;\n            var event = this.mockControl.createMock({\n                stopImmediatePropagation: function() {}\n            });\n\n            event.which = LEFT;\n\n            this.mockControl.reset();\n            this.mockGrid.expects().getActiveCell().andReturn({row: 12, cell: 2});\n            this.gridInteractionHandler.handleKeyDown(event);\n            this.mockControl.verify();\n\n            this.mockControl.reset();\n            this.mockGrid.expects().getActiveCell().andReturn({row: 12, cell: 1});\n            event.expects().stopImmediatePropagation();\n            this.gridInteractionHandler.handleKeyDown(event);\n            this.mockControl.verify();\n\n            event.which = UP;\n\n            this.mockGrid.expects().getActiveCell().andReturn({row: 1, cell: 1});\n            this.gridInteractionHandler.handleKeyDown(event);\n            this.mockControl.verify();\n\n            this.mockGrid.expects().getActiveCell().andReturn({row: 0, cell: 1});\n            event.expects().stopImmediatePropagation();\n            this.gridInteractionHandler.handleKeyDown(event);\n            this.mockControl.verify();\n\n        },\n\n        testHandleKeyDown_PageDownShouldMoveActiveCellAndScrollRowIntoViewAndSwallowEventIfThereIsSpace: function () {\n\n            var PAGE_DOWN = 34;\n            var event = this.mockControl.createMock({\n                stopImmediatePropagation: function() {}\n            });\n            event.which = PAGE_DOWN;\n\n            this.mockGrid.expects().getViewport().andReturn(\n                {top: 200, bottom: 300}\n            );\n            this.mockGrid.expects().getActiveCell().andReturn(\n                {cell: 111, row: 222}\n            );\n            this.mockGrid.expects().getDataLength().andReturn(1000);\n            this.mockGrid.expects().scrollRowIntoView(299, true);\n            this.mockGrid.expects().gotoCell(222 + (100 - 1), 111);\n            event.expects().stopImmediatePropagation();\n\n            this.gridInteractionHandler.handleKeyDown(event);\n\n            this.mockControl.verify();\n        },\n\n        testHandleKeyDown_PageDownShouldMoveActiveCellAndNotScrollRowIntoViewAndSwallowEventIfThereIsNoSpace: function () {\n\n            var PAGE_DOWN = 34;\n            var event = this.mockControl.createMock({\n                stopImmediatePropagation: function() {}\n            });\n            event.which = PAGE_DOWN;\n\n            this.mockGrid.expects().getViewport().andReturn(\n                { top: 400, bottom: 500 }\n            );\n            this.mockGrid.expects().getActiveCell().andReturn(\n                { cell: 111, row: 450 }\n            );\n            this.mockGrid.expects().getDataLength().andReturn(500);\n            this.mockGrid.expects().gotoCell(450 + (100 - 1), 111);\n            event.expects().stopImmediatePropagation();\n\n            this.gridInteractionHandler.handleKeyDown(event);\n\n            this.mockControl.verify();\n        },\n\n        testHandleKeyDown_PageUpShouldScrollRowIntoViewMoveActiveCellAndSwallowEvent: function () {\n\n            var PAGE_UP = 33;\n            var event = this.mockControl.createMock({\n                stopImmediatePropagation: function() {}\n            });\n            event.which = PAGE_UP;\n\n            this.mockGrid.expects().getViewport().andReturn(\n                {top: 200, bottom: 300}\n            );\n            this.mockGrid.expects().getActiveCell().andReturn(\n                {cell: 111, row: 222}\n            );\n            this.mockGrid.expects().scrollRowIntoView(101, false);\n            this.mockGrid.expects().gotoCell(222 - (100 - 1), 111);\n            event.expects().stopImmediatePropagation();\n\n            this.gridInteractionHandler.handleKeyDown(event);\n\n            this.mockControl.verify();\n        },\n\n        testHandleKeyDown_PageUpShouldScrollRowIntoViewWithoutJumpingAboveTopOfGridMoveActiveCellAndSwallowEvent: function () {\n\n            var PAGE_UP = 33;\n            var event = this.mockControl.createMock({\n                stopImmediatePropagation: function() {}\n            });\n            event.which = PAGE_UP;\n\n            this.mockGrid.expects().getViewport().andReturn(\n                {top: 5, bottom: 105}\n            );\n            this.mockGrid.expects().getActiveCell().andReturn(\n                {cell: 111, row: 75}\n            );\n            this.mockGrid.expects().scrollRowIntoView(0, false);\n            this.mockGrid.expects().gotoCell(75 - (100 - 1), 111);\n            event.expects().stopImmediatePropagation();\n\n            this.gridInteractionHandler.handleKeyDown(event);\n\n            this.mockControl.verify();\n        },\n\n        make_testHandleKeyDown_ControlHomeShouldGoToA1AndSwallowEvent: function (modifier) {\n\n            var HOME = 36;\n            var event = this.mockControl.createMock({\n                stopImmediatePropagation: function() {}\n            });\n            event.which = HOME;\n            event[modifier] = true;\n\n            this.mockGrid.expects().gotoCell(0, 1);\n            event.expects().stopImmediatePropagation();\n\n            this.gridInteractionHandler.handleKeyDown(event);\n\n            this.mockControl.verify();\n        },\n        testHandleKeyDown_ControlHomeShouldGoToA1AndSwallowEvent : function () {\n            this.make_testHandleKeyDown_ControlHomeShouldGoToA1AndSwallowEvent('ctrlKey');\n        },\n        testHandleKeyDown_MacMetaHomeShouldGoToA1AndSwallowEvent : function () {\n            this.make_testHandleKeyDown_ControlHomeShouldGoToA1AndSwallowEvent('metaKey');\n        },\n\n        testHandleKeyDown_HomeWithoutControlShouldNotGoToACellOrSwallowEvent: function () {\n\n            var HOME = 36;\n            var event = this.mockControl.createMock({\n                stopImmediatePropagation: function() {}\n            });\n            event.which = HOME;\n            event.ctrlKey = false;\n            event.metaKey = false;\n\n            this.gridInteractionHandler.handleKeyDown(event);\n\n            this.mockControl.verify();\n        },\n\n        make_testHandleKeyDown_ControlEndShouldGoToBottomRightAndSwallowEvent: function (modifier) {\n            var END = 35;\n            var event = this.mockControl.createMock({\n                stopImmediatePropagation: function() {}\n            });\n            event.which = END;\n            event[modifier] = true;\n\n            this.mockGrid.expects().getDataLength().andReturn(1000);\n            this.mockGrid.expects().getColumns().andReturn(\n                { length: 53 }\n            );\n            this.mockGrid.expects().gotoCell(999, 52);\n            event.expects().stopImmediatePropagation();\n\n            this.gridInteractionHandler.handleKeyDown(event);\n\n            this.mockControl.verify();\n        },\n        testHandleKeyDown_ControlEndShouldGoToBottomRightAndSwallowEvent: function () {\n            this.make_testHandleKeyDown_ControlEndShouldGoToBottomRightAndSwallowEvent('ctrlKey');\n        },\n        testHandleKeyDown_MacMetaEndShouldGoToBottomRightAndSwallowEvent: function () {\n            this.make_testHandleKeyDown_ControlEndShouldGoToBottomRightAndSwallowEvent('metaKey');\n        },\n\n        testHandleKeyDown_EndWithoutControlShouldNotGoToACellOrSwallowEvent: function () {\n\n            var END = 35;\n            var event = this.mockControl.createMock({\n                stopImmediatePropagation: function() {}\n            });\n            event.which = END;\n            event.ctrlKey = false;\n            event.metaKey = false;\n\n            this.gridInteractionHandler.handleKeyDown(event);\n\n            this.mockControl.verify();\n        },\n\n        testHandleKeyDown_F2EntersEditModeAndDoesNotSwallowEvent: function () {\n            var F2 = 113;\n            var event = this.mockControl.createMock({\n                stopImmediatePropagation: function() {}\n            });\n            event.which = F2;\n\n            this.mockGrid.expects().editActiveCell();\n\n            this.gridInteractionHandler.handleKeyDown(event);\n\n            this.mockControl.verify();\n        },\n\n        make_testHandleKeyDown_CtrlX_ShouldStopPropagationIfCutActionReturnsTrue: function (modifier) {\n            var LETTER_X = 88;\n            var event = this.mockControl.createMock({\n                stopImmediatePropagation: function() {}\n            });\n\n            this.mockGridCommands.expects().cut().andReturn(true);\n            event.expects().stopImmediatePropagation();\n\n            event.which = LETTER_X;\n            event[modifier] = true;\n\n            this.gridInteractionHandler.handleKeyDown(event);\n\n            this.mockControl.verify();\n        },\n        testHandleKeyDown_CtrlX_WhenNotEditing_PerformsCutAndStopsPropagation: function () {\n            this.make_testHandleKeyDown_CtrlX_ShouldStopPropagationIfCutActionReturnsTrue('ctrlKey');\n        },\n        testHandleKeyDown_MacMetaX_WhenNotEditing_PerformsCutAndStopsPropagation: function () {\n            this.make_testHandleKeyDown_CtrlX_ShouldStopPropagationIfCutActionReturnsTrue('metaKey');\n        },\n\n        make_testHandleKeyDown_CtrlX_ShouldNotStopPropagationIfCutActionReturnsFalse: function (modifier) {\n            var LETTER_X = 88;\n            var event = this.mockControl.createMock({\n                stopImmediatePropagation: function() {}\n            });\n\n            this.mockGridCommands.expects().cut().andReturn(false);\n\n            event.which = LETTER_X;\n            event[modifier] = true;\n\n            this.gridInteractionHandler.handleKeyDown(event);\n\n            this.mockControl.verify();\n        },\n        testHandleKeyDown_CtrlX_WhenEditing_DoesNothing: function () {\n            this.make_testHandleKeyDown_CtrlX_ShouldNotStopPropagationIfCutActionReturnsFalse('ctrlKey');\n        },\n        testHandleKeyDown_MacMetaKeyX_WhenEditing_DoesNothing: function () {\n            this.make_testHandleKeyDown_CtrlX_ShouldNotStopPropagationIfCutActionReturnsFalse('metaKey');\n        },\n\n        make_testHandleKeyDown_CtrlC_StopsPropagationIfCopyActionReturnsTrue: function (modifier) {\n            var LETTER_C = 67;\n            var event = this.mockControl.createMock({\n                stopImmediatePropagation: function() {}\n            });\n            this.mockGridCommands.expects().copy().andReturn(true);\n            event.expects().stopImmediatePropagation();\n\n            event.which = LETTER_C;\n            event[modifier] = true;\n\n            this.gridInteractionHandler.handleKeyDown(event);\n\n            this.mockControl.verify();\n        },\n        testHandleKeyDown_CtrlC_WhenNotEditing_PerformsCopyAndStopsPropagation: function () {\n            this.make_testHandleKeyDown_CtrlC_StopsPropagationIfCopyActionReturnsTrue('ctrlKey');\n        },\n        testHandleKeyDown_MacMetaC_WhenNotEditing_PerformsCopyAndStopsPropagation: function () {\n            this.make_testHandleKeyDown_CtrlC_StopsPropagationIfCopyActionReturnsTrue('metaKey');\n        },\n\n        make_testHandleKeyDown_CtrlC_DoesNotStopPropagationIfCopyActionReturnsFalse: function (modifier) {\n            var LETTER_C = 67;\n            var event = this.mockControl.createMock({\n                stopImmediatePropagation: function() {}\n            });\n            this.mockGridCommands.expects().copy().andReturn(false);\n\n            event.which = LETTER_C;\n            event[modifier] = true;\n\n            this.gridInteractionHandler.handleKeyDown(event);\n\n            this.mockControl.verify();\n        },\n        testHandleKeyDown_CtrlC_WhenEditing_DoesNothing: function () {\n            this.make_testHandleKeyDown_CtrlC_DoesNotStopPropagationIfCopyActionReturnsFalse('ctrlKey');\n        },\n        testHandleKeyDown_MacMetaC_WhenEditing_DoesNothing: function () {\n            this.make_testHandleKeyDown_CtrlC_DoesNotStopPropagationIfCopyActionReturnsFalse('metaKey');\n        },\n\n        make_testHandleKeyDown_CtrlV_StopsPropagationIfPasteActionReturnsTrue: function (modifier) {\n            var LETTER_V = 86;\n            var event = this.mockControl.createMock({\n                stopImmediatePropagation: function() {}\n            });\n\n            this.mockGridCommands.expects().paste().andReturn(true);\n            event.expects().stopImmediatePropagation();\n\n            event.which = LETTER_V;\n            event[modifier] = true;\n\n            this.gridInteractionHandler.handleKeyDown(event);\n\n            this.mockControl.verify();\n        },\n        testHandleKeyDown_CtrlV_WhenNotEditing_PerformsPasteAndStopsPropagation: function () {\n            this.make_testHandleKeyDown_CtrlV_StopsPropagationIfPasteActionReturnsTrue('ctrlKey');\n        },\n        testHandleKeyDown_MacMetaV_WhenNotEditing_PerformsPasteAndStopsPropagation: function () {\n            this.make_testHandleKeyDown_CtrlV_StopsPropagationIfPasteActionReturnsTrue('metaKey');\n        },\n\n        make_testHandleKeyDown_CtrlV_DoesntStopPropagationIfPasteActionReturnsFalse: function (modifier) {\n            var LETTER_V = 86;\n            var event = this.mockControl.createMock({\n                stopImmediatePropagation: function() {}\n            });\n            this.mockGridCommands.expects().paste().andReturn(false);\n\n            event.which = LETTER_V;\n            event[modifier] = true;\n\n            this.gridInteractionHandler.handleKeyDown(event);\n\n            this.mockControl.verify();\n        },\n        testHandleKeyDown_CtrlV_WhenEditing_DoesNothing: function () {\n            this.make_testHandleKeyDown_CtrlV_DoesntStopPropagationIfPasteActionReturnsFalse('ctrlKey');\n        },\n        testHandleKeyDown_MacMetaV_WhenEditing_DoesNothing: function () {\n            this.make_testHandleKeyDown_CtrlV_DoesntStopPropagationIfPasteActionReturnsFalse('metaKey');\n        },\n\n        make_testHandleKeyDown_DeleteyKey_StopsPropagationIfClearActionReturnsTrue: function (deleteyKey) {\n            var event = this.mockControl.createMock({\n                stopImmediatePropagation: function() {}\n            });\n\n            this.mockGridCommands.expects().clear().andReturn(true);\n            event.expects().stopImmediatePropagation();\n\n            event.which = deleteyKey;\n            this.gridInteractionHandler.handleKeyDown(event);\n\n            this.mockControl.verify();\n        },\n        testHandleKeyDown_Del_StopsPropagationIfClearActionReturnsTrue: function() {\n            var DEL = 46;\n            this.make_testHandleKeyDown_DeleteyKey_StopsPropagationIfClearActionReturnsTrue(DEL);\n        },\n        testHandleKeyDown_Backspace_StopsPropagationIfClearActionReturnsTrue: function() {\n            var BACKSPACE = 8;\n            this.make_testHandleKeyDown_DeleteyKey_StopsPropagationIfClearActionReturnsTrue(BACKSPACE);\n        },\n\n        make_testHandleKeyDown_DeleteyKey_DoesntStopPropagationIfClearActionReturnsFalse: function (deleteyKey) {\n            var event = this.mockControl.createMock({\n                stopImmediatePropagation: function() {}\n            });\n            this.mockGridCommands.expects().clear().andReturn(false);\n\n            event.which = deleteyKey;\n            this.gridInteractionHandler.handleKeyDown(event);\n\n            this.mockControl.verify();\n        },\n        testHandleKeyDown_Del_DoesntStopPropagationIfClearActionReturnsFalse: function() {\n            var DEL = 46;\n            this.make_testHandleKeyDown_DeleteyKey_DoesntStopPropagationIfClearActionReturnsFalse(DEL);\n        },\n        testHandleKeyDown_Backspace_DoesntStopPropagationIfClearActionReturnsFalse: function() {\n            var BACKSPACE = 8;\n            this.make_testHandleKeyDown_DeleteyKey_DoesntStopPropagationIfClearActionReturnsFalse(BACKSPACE);\n        }\n\n    }),\n\n\n    new YAHOO.tool.TestCase({\n\n        name: \"testGridDivKeyPressHandler\",\n\n        setUp: function() {\n            var mockJQuery = function(){ return {\n                keypress: function(){}               \n            } };\n            this.mockControl = new MockControl();\n            this.mockGrid = this.mockControl.createMock({\n                getCellEditor : function() {},\n                editActiveCell: function() {}\n            });\n            this.mockGrid.onKeyDown = {\n                subscribe: function() {}\n            };\n            this.mockGrid.onColumnsResized = {\n                subscribe: function() {}\n            };\n            this.mockGrid.onCellChange = {\n                subscribe: function() {}\n            };\n            this.mockGrid.onActiveCellChanged = {\n                subscribe: function() {}\n            };\n            this.mockGrid.onClick = {\n                subscribe: function() {}\n            };\n            this.mockCellEditor = this.mockControl.createMock({\n                setInputElementValues : function() {}\n            });\n            mockGridCommands = { resizeColumns: null };\n\n            this.gridIH = new Dirigible.GridInteractionHandler(\n                mockJQuery, this.mockGrid, mockGridCommands\n            );\n        },\n\n        testGridDivKeyPressHandlerHandlesCharacterKeysAndReturnsFalseIfNoCellEditor: function() {\n            this.mockGrid.expects().getCellEditor().andReturn(null);\n            this.mockGrid.expects().editActiveCell();\n            this.mockGrid.expects().getCellEditor().andReturn(this.mockCellEditor);\n            this.mockCellEditor.expects().setInputElementValues('a');\n\n            var bubble = this.gridIH.handleGridDivKeyPress({which: 97});\n\n            this.mockControl.verify();\n            YAHOO.util.Assert.areSame(false, bubble);\n        },\n\n        testGridDivKeyPressHandlerHandlesCharacterKeysAndBubblesIfThereIsACellEditor: function() {\n            this.mockGrid.expects().getCellEditor().andReturn(this.mockCellEditor);\n\n            var bubble = this.gridIH.handleGridDivKeyPress({which: 97});\n\n            this.mockControl.verify();\n            YAHOO.util.Assert.areSame(true, bubble);\n\n            this.mockControl.reset();\n            this.mockGrid.expects().getCellEditor().andReturn(this.mockCellEditor);\n            // 32 is important because it is space and lives among the non-printables\n            //  see testGridDivKeyPressHandlerHandlesNonCharacterKeys\n            bubble = this.gridIH.handleGridDivKeyPress({which: 32});\n\n            this.mockControl.verify();\n            YAHOO.util.Assert.areSame(true, bubble);\n        },\n\n        testGridDivKeyPressHandlerHandlesNonCharacterKeys: function() {\n            for (var keyCode= 0; keyCode <= 47; keyCode++) {\n                if (keyCode != 32) {\n                    var bubble = this.gridIH.handleGridDivKeyPress(\n                        {which: keyCode}\n                    );\n                    this.mockControl.verify();\n                    YAHOO.util.Assert.areSame(true, bubble);\n                    this.mockControl.reset();\n                }\n            }\n        },\n\n        testGridDivKeyPressHandlerSkipsAndReturnsTrueForCtrl: function() {\n            var bubble = this.gridIH.handleGridDivKeyPress(\n                {which: 97, ctrlKey: true}\n            );\n            this.mockControl.verify();\n            YAHOO.util.Assert.areSame(true, bubble);\n        },\n\n        testGridDivKeyPressHandlerSkipsAndReturnsTrueForAlt: function() {\n            var bubble = this.gridIH.handleGridDivKeyPress(\n                {which: 97, altKey: true}\n            );\n            this.mockControl.verify();\n            YAHOO.util.Assert.areSame(true, bubble);\n        }\n    }),\n\n    new YAHOO.tool.TestCase({\n\n        name: \"testUpdateFormulaBar\",\n\n        setUp: function() {\n            $(\"#id_editor_container\").append(\"<input id='id_formula_bar'></input>\");\n            $('#id_formula_bar').val(\"Some random value that we won't use in a test\");\n            this.mockControl = new MockControl();\n            this.mockGrid = this.mockControl.createMock({\n                getActiveCell: function() {},\n                getCellFormula: function() {}\n            });\n            this.mockData = {};\n        },\n\n        tearDown: function() {\n            $('#id_formula_bar').remove();\n        },\n\n        testUpdateFormulaBarShouldUpdateItWithCellFormula: function () {\n            var slickCellLocation = { row: 4, cell: 16 };\n            this.mockGrid.expects().getActiveCell().andReturn(\n                slickCellLocation\n            );\n            this.mockGrid.expects().getCellFormula(slickCellLocation).andReturn(\n                \"=A1\"\n            );\n\n            var gridIH = new Dirigible.GridInteractionHandler($, this.mockGrid, null);\n            gridIH.updateFormulaBar();\n\n            this.mockControl.verify();\n            YAHOO.util.Assert.areSame(\n                \"=A1\",\n                $('#id_formula_bar').val()\n            );\n        },\n\n        testUpdateFormulaBarShouldMakeFormulaBarBlankIfNoCellFormula: function () {\n            var slickCellLocation = { row: 4, cell: 16 };\n            this.mockGrid.expects().getActiveCell().andReturn(\n                slickCellLocation\n            );\n            this.mockGrid.expects().getCellFormula(slickCellLocation).andReturn(\n                \"\"\n            );\n            // Start off with something to clear\n            $('#id_formula_bar').val(\"an old value\");\n\n            var gridIH = new Dirigible.GridInteractionHandler($, this.mockGrid, null);\n            gridIH.updateFormulaBar();\n\n            this.mockControl.verify();\n            YAHOO.util.Assert.areSame(\n                \"\",\n                $('#id_formula_bar').val()\n            );\n        },\n\n        testUpdateFormulaBarShouldDoNothingIfNoActiveCell: function () {\n            this.mockGrid.expects().getActiveCell().andReturn(\n                null\n            );\n\n            var gridIH = new Dirigible.GridInteractionHandler($, this.mockGrid, null);\n            gridIH.updateFormulaBar();\n\n            this.mockControl.verify();\n        }\n\n    }),\n\n    new YAHOO.tool.TestCase({\n\n        name: \"testFormulaBarClickHandler\",\n\n        setUp: function() {\n            $(\"#id_editor_container\").append(\"<input id='id_formula_bar'></input>\");\n            $('#id_formula_bar').val(\"Some random value that we won't use in a test\");\n            this.mockControl = new MockControl();\n            this.mockGrid = this.mockControl.createMock({\n                getCellEditor : function() {},\n                editActiveCell: function() {}\n            });\n            this.mockCellEditor = this.mockControl.createMock();\n        },\n\n        testFormulaBarClickHandlerDoesNotEditCellIfItsAlreadyEditing: function() {\n            this.mockGrid.expects().getCellEditor().andReturn(this.mockCellEditor);\n\n            var gridIH = new Dirigible.GridInteractionHandler($, this.mockGrid, null);\n            gridIH.handleFormulaBarClick();\n\n            this.mockControl.verify();\n            YAHOO.util.Assert.areSame($('#id_formula_bar')[0], document.activeElement);\n        },\n\n        testFormulaBarClickHandlerEditsCellIfItNeedsIt: function() {\n            this.mockGrid.expects().getCellEditor().andReturn(null);\n            this.mockGrid.expects().editActiveCell();\n\n            var gridIH = new Dirigible.GridInteractionHandler($, this.mockGrid, null);\n            gridIH.handleFormulaBarClick();\n\n            this.mockControl.verify();\n            YAHOO.util.Assert.areSame($('#id_formula_bar')[0], document.activeElement);\n        },\n\n        testFormulaBarClickHandlerCallsCaretToRestoreCursorAndSelectionPosition: function() {\n            // handleFormulaBarClick needs to call caret as is tested here in order\n            // to restore cursor position. We removed this logic once, thinking it\n            // not needed, but caused T2633 to break in IE, with a fail that is\n            // easy to reproduce manually. So don't remove the calls to caret without\n            // running all FTs.\n            var mockJQuery = this.mockControl.createMock({\n                find: function() {}\n            });\n            var mockFormulaBar = this.mockControl.createMock({\n                caret: function(){},\n                focus: function(){}\n            });\n            this.mockGrid.expects().getCellEditor().andReturn(null);\n            this.mockGrid.expects().editActiveCell();\n            mockJQuery.expects().find('#id_formula_bar').andReturn(mockFormulaBar);\n            mockFormulaBar.expects().caret().andReturn({start:1, end:321});\n            mockFormulaBar.expects().focus();\n            mockFormulaBar.expects().caret(1, 321);\n\n            var gridIH = new Dirigible.GridInteractionHandler(\n                mockJQuery.find, this.mockGrid, null);\n            gridIH.handleFormulaBarClick();\n\n            this.mockControl.verify();\n        }\n    })\n\n];\n</script>\n\n<script src=\"yuirunner.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/tests/grid_remote_model_test.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n<html>\n<head>\n    <script type=\"text/javascript\" src=\"/static/jquery/jquery-1.4.2.min.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/dirigible/tests/test_utils.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/json/json2.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/slickgrid/slick.core.js\"></script>\n    <script type=\"text/javascript\" src=\"yuitest/yuitest-combo.js\"></script>\n    <script type=\"text/javascript\" src=\"jsmock.js\"></script>\n\n    <script type=\"text/javascript\" src=\"/static/dirigible/scripts/grid_remote_model.js\"></script>\n\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"logger.css\" />\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"testlogger.css\" />\n</head>\n\n<body>\n<div id=\"id_results\">Please wait</div>\n\n\n<script type=\"text/javascript\">\n\n    tests = [\n\n        new YAHOO.tool.TestCase({\n\n            name: \"testGridRemoteModel\",\n\n            setUp: function () {\n                this.mockControl = new MockControl();\n                this.mockGrid = this.mockControl.createMock({});\n                this.urls = { getJSONGridData: 'get_json_url' };\n \n            },\n\n            testInitialisation: function () {\n                var remoteModel = new Dirigible.GridRemoteModel(this.urls);\n                assertDeepAreSame({}, remoteModel.patches);\n                assertDeepAreSame({}, remoteModel.pending);\n            },\n\n            testGetDataGetsJsonForRangeAndUsesCurriedOnSuccess: function () {\n                var remoteModel = new Dirigible.GridRemoteModel(this.urls);\n\n                var mockJQuery = this.mockControl.createMock({\n                    getJSON : function () {}\n                });\n                $.extend(true, jQuery, mockJQuery);\n\n                var mockRemoteModel = this.mockControl.createMock({\n                    getSuccessHandlerForPatch : function () {}\n                });\n                $.extend(true, remoteModel, mockRemoteModel);\n\n                var patch = 'patch';\n                mockRemoteModel.expects().getSuccessHandlerForPatch(patch).andReturn('curried handler');\n\n                var called_with = {};\n                mockJQuery.expects().getJSON(\n                    TypeOf.isA(String),\n                    TypeOf.isA(Object),\n                    'curried handler'\n                ).andStub(\n                    function () {\n                        called_with = [\n                            arguments[0], arguments[1]\n                        ];\n                    }\n                );\n\n                var left = 1;\n                var topmost = 2;\n                var right = 3;\n                var bottom = 4;\n                remoteModel.getData(left, topmost, right, bottom, patch);\n\n                this.mockControl.verify();\n\n                var expected_call = [\n                    this.urls.getJSONGridData,\n                    {range: left + ', ' + topmost + ', ' + right + ', ' + bottom},\n                ];\n\n                assertDeepAreSame(expected_call, called_with);\n            },\n\n\n            testCellToPatch: function () {\n                var remoteModel = new Dirigible.GridRemoteModel(this.urls);\n\n                expected_results = {\n                    '-1, -1': {patchCol: 0, patchRow: 0},\n                    '0, 0': {patchCol: 0, patchRow: 0},\n                    '1, 1': {patchCol: 0, patchRow: 0},\n                    '26, 1': {patchCol: 0, patchRow: 0},\n                    '1, 100': {patchCol: 0, patchRow: 0},\n                    '26, 100': {patchCol: 0, patchRow: 0},\n                    '26, 101': {patchCol: 0, patchRow: 1},\n                    '27, 100': {patchCol: 1, patchRow: 0},\n                    '52, 100': {patchCol: 1, patchRow: 0},\n                    '27, 200': {patchCol: 1, patchRow: 1},\n                    '52, 200': {patchCol: 1, patchRow: 1},\n                    '27, 101': {patchCol: 1, patchRow: 1},\n                    '52, 201': {patchCol: 1, patchRow: 2}\n                }\n                for(var key in expected_results) {\n                    var coords = key.split(', ');\n                    col = parseInt(coords[0]);\n                    row = parseInt(coords[1]);\n                    assertDeepAreSame(\n                        expected_results[key],\n                        remoteModel.cellToPatch(col, row),\n                        'incorrect for ' + key\n                    );\n                }\n            },\n\n            testPatchToCells: function () {\n                var remoteModel = new Dirigible.GridRemoteModel(this.urls);\n\n                expected_results = {\n                    '0, 0': {left:1, topmost:1, right:26, bottom:100},\n                    '1, 0': {left:27, topmost:1, right:52, bottom:100},\n                    '0, 1': {left:1, topmost:101, right:26, bottom:200},\n                    '1, 1': {left:27, topmost:101, right:52, bottom:200},\n                    '2, 2': {left:53, topmost:201, right:78, bottom:300}\n                };\n                for(var key in expected_results) {\n                    var coords = key.split(', ');\n                    col = parseInt(coords[0]);\n                    row = parseInt(coords[1]);\n                    assertDeepAreSame(\n                        expected_results[key],\n                        remoteModel.patchToCells( {patchCol:col, patchRow:row} ),\n                        'incorrect for ' + key\n                    );\n                };\n            },\n\n\n            testChoosePatchesToLoad: function () {\n                var remoteModel = new Dirigible.GridRemoteModel(this.urls);\n\n                expected_results = {\n                    '1, 1, 1, 1': [{patchCol: 0, patchRow: 0}],\n                    '1, 1, 26, 100': [{patchCol: 0, patchRow: 0}],\n                    '26, 100, 27, 101': [\n                        {patchCol: 0, patchRow: 0},\n                        {patchCol: 0, patchRow: 1},\n                        {patchCol: 1, patchRow: 0},\n                        {patchCol: 1, patchRow: 1}\n                    ],\n                    '10, 230, 52, 600': [\n                        {patchCol: 0, patchRow: 2},\n                        {patchCol: 0, patchRow: 3},\n                        {patchCol: 0, patchRow: 4},\n                        {patchCol: 0, patchRow: 5},\n                        {patchCol: 1, patchRow: 2},\n                        {patchCol: 1, patchRow: 3},\n                        {patchCol: 1, patchRow: 4},\n                        {patchCol: 1, patchRow: 5},\n                    ]\n                }\n\n                for(var key in expected_results) {\n                    var coords = key.split(', ');\n                    left = parseInt(coords[0]);\n                    topmost = parseInt(coords[1]);\n                    right = parseInt(coords[2]);\n                    bottom = parseInt(coords[3]);\n                    assertDeepAreSame(\n                        expected_results[key],\n                        remoteModel.choosePatchesToLoad(left, topmost, right, bottom),\n                        'incorrect for ' + key\n                    );\n                }\n            },\n\n            testIsPatchLoadedOrPending: function () {\n                var remoteModel = new Dirigible.GridRemoteModel(this.urls);\n                $.extend(true, remoteModel.patches, {\n                      0: {\n                        0: true,\n                        5: true,\n                        6: true\n                      }\n                    }\n                );\n                $.extend(true, remoteModel.pending, {\n                      0: {\n                        1: true\n                      },\n                      2: {\n                        2: true\n                      }\n                    }\n                );\n\n                expected_results = {\n                    '0, 0': true,\n                    '0, 1': true,\n                    '0, 2': false,\n                    '1, 15': false,\n                    '2, 2': true,\n                    '2, 3': false\n                }\n                for(var key in expected_results) {\n                    var coords = key.split(', ');\n                    var col = parseInt(coords[0]);\n                    var row = parseInt(coords[1]);\n                    var patch = {patchCol : col, patchRow: row};\n                    YAHOO.util.Assert.areSame(\n                        expected_results[key],\n                        remoteModel.isPatchLoadedOrPending(patch),\n                        'incorrect for ' + key\n                    );\n                }\n            },\n\n            testLoadPatchIfNecessary: function () {\n                var remoteModel = new Dirigible.GridRemoteModel(this.urls);\n                var mockPatches = { 0: { 0: true } };\n                $.extend(true, remoteModel.patches, mockPatches);\n                var remoteModelMockMixins = this.mockControl.createMock({\n                    getData: function () {}\n                });\n                $.extend(true, remoteModel, remoteModelMockMixins);\n\n                var mockOnDataLoading = this.mockControl.createMock(\n                    {notify: function() {}}\n                );\n                remoteModel.onDataLoading = mockOnDataLoading;\n\n                remoteModel.loadPatchIfNecessary( {patchCol:0, patchRow:0} );\n                this.mockControl.verify();\n\n                assertDeepAreSame({}, remoteModel.pending, 'pending not correct after not loading');\n\n                var newPatch =  {patchCol:1, patchRow:1};\n                mockOnDataLoading.expects().notify();\n                remoteModelMockMixins.expects().getData(27, 101, 52, 200, newPatch);\n\n                remoteModel.loadPatchIfNecessary(newPatch);\n\n                this.mockControl.verify();\n\n                assertDeepAreSame({1: {1: true}}, remoteModel.pending, 'pending not correct after loading');\n            },\n\n            testAddData: function () {\n                var remoteModel = new Dirigible.GridRemoteModel(this.urls);\n                var previousData = [\n                    { header : 1 },\n                    { header : 2,\n                        3:{formula : '=C2', formatted_value : 'old'}\n                    },\n                    { header : 3,\n                        2:{formula : '=B3', formatted_value : 'old'},\n                        3:{formula : '=C3', formatted_value : 'old'},\n                        4:{formula : '=D3', formatted_value : 'old'}\n                    },\n                    { header : 4 }, \n                    { header : 5 }, \n                    { header : 6 }, \n                    { header : 7, \n                        1: {formula : '=A7', formatted_value : 'old'},\n                        2: {formula : '=B7', formatted_value : 'old'},\n                        3: {formula : '=C7', formatted_value : 'old'}\n                    }\n                ]\n\n                var newData = {\n                    \"width\": 52,\n                    \"console_text\": \"Took 0.00s\", \n                    \"3\": {\n                        \"3\":{formula: '=C3', formatted_value: 'replaced'}\n                    },\n                    \"6\": {\n                        \"2\": {formula: '=B6', formatted_value: 'new cell'},\n                        \"3\": {formula : '=C6', formatted_value: 'new cell'}\n                    },\n                    \"8\": {\n                        \"2\": {formula: '=B8', formatted_value: 'new row'}\n                    },\n                    'left': 2,\n                    'topmost': 3,\n                    'right': 3,\n                    'bottom': 8\n                }\n\n                var expectedData =[\n                    { header : 1 },\n                    { header : 2,\n                        3:{formula : '=C2', formatted_value : 'old'}\n                    },\n                    { header : 3,\n                        3:{formula : '=C3', formatted_value : 'replaced'},\n                        4:{formula : '=D3', formatted_value : 'old'}\n                    },\n                    { header : 4 }, \n                    { header : 5 }, \n                    { header : 6, \n                        2: {formula : '=B6', formatted_value : 'new cell'},\n                        3: {formula : '=C6', formatted_value : 'new cell'}\n                    },\n                    { header : 7, \n                        1: {formula : '=A7', formatted_value : 'old'}\n                    },\n                    { header : 8, \n                        2: {formula : '=B8', formatted_value : 'new row'}\n                    }\n                ]; \n\n                remoteModel.data = previousData;\n                remoteModel.addData(newData);\n\n                assertDeepAreSame(expectedData, remoteModel.data);\n            },\n\n            testOnSuccessAddsDataAndNotifiesOnDataLoadedEvent: function () {\n                var subscriberCalled = false;\n                var addDataArgs;\n                var remoteModel = new Dirigible.GridRemoteModel(this.urls);\n                var mockRemoteModel = this.mockControl.createMock({\n                        'addData': function() {}\n                });\n\n                $.extend(true, remoteModel, mockRemoteModel);\n                mockRemoteModel.expects().addData(TypeOf.isA(Object)).andStub(\n                        function() {\n                        addDataArgs = arguments[0];\n                   }\n                        );\n               remoteModel.onDataLoaded.subscribe(\n                   function(e , range) {\n                       subscriberCalled = true;\n                       YAHOO.util.Assert.areSame(20, range.left, 'left not passed');\n                       YAHOO.util.Assert.areSame(30, range.topmost, 'topmost not passed');\n                       YAHOO.util.Assert.areSame(40, range.right, 'right not passed');\n                       YAHOO.util.Assert.areSame(50, range.bottom, 'bottom not passed');\n               });\n               var jsonData = {\n                   'left': 20,\n                   'topmost': 30,\n                   'right': 40,\n                   'bottom': 50\n               };\n               remoteModel.onSuccess( jsonData );\n\n               this.mockControl.verify();\n\n               YAHOO.util.Assert.isTrue(subscriberCalled, 'dataloaded subscriber not notified');\n               \n               assertDeepAreSame(jsonData, addDataArgs);\n\n            },\n\n            testGetSuccessHandlerForPatchCurriesPatchIntoOnSuccess: function () {\n                var remoteModel = new Dirigible.GridRemoteModel(this.urls);\n\n                var mockRemoteModel = this.mockControl.createMock({\n                         'onSuccess': function() {}\n                 });\n\n                $.extend(true, remoteModel, mockRemoteModel);\n\n                remoteModel.patches = {0: {1: true}};\n                remoteModel.pending = {1: {2: true}, 3: {5: true}};\n                var forPatch = {patchCol: 1, patchRow: 2};\n                var curriedOnSuccess = remoteModel.getSuccessHandlerForPatch(forPatch);\n                assertDeepAreSame({0: {1: true}}, remoteModel.patches,\n                    'patches updated at curry-time (too early! need to wait til success function actually called');\n\n                var jsonData = {'foo': 1};\n                mockRemoteModel.expects().onSuccess(jsonData);\n\n                curriedOnSuccess(jsonData);\n\n                this.mockControl.verify();\n                assertDeepAreSame(\n                    {\n                        0: {1: true},\n                        1: {2: true}\n                    },\n                    remoteModel.patches\n                );\n                assertDeepAreSame({1: {}, 3: {5: true}}, remoteModel.pending);\n            },\n\n            testCurriedSuccessHandlerChecksJsonDataCoversItsWholeExpectedPatch: function () {\n                var remoteModel = new Dirigible.GridRemoteModel(this.urls);\n                remoteModel.addData = function () {};\n\n                var patchesBefore = {0: {1: true}};\n                var patchesAfter = {0: {1: true}, 1: {2: true}};\n\n                var forPatch = {patchCol: 1, patchRow: 2};\n                var expectedJson = {left:27, topmost:201, right:52, bottom:300};\n                var curriedOnSuccess = remoteModel.getSuccessHandlerForPatch(forPatch);\n\n                var smallJson = {left: 27, topmost:202, right:52, bottom:300};\n                var bigJson = {left: 0, topmost:55, right:52, bottom:300};\n                var cases = [smallJson, expectedJson, bigJson];\n                var results = [\n                    patchesBefore,\n                    patchesAfter,\n                    patchesAfter\n                ];\n                for (var i=0; i<cases.length; i++){\n                    remoteModel.patches = $.extend(true, {}, patchesBefore);\n                    curriedOnSuccess(cases[i]);\n                    assertDeepAreSame(results[i], remoteModel.patches);\n                }\n            },\n\n            testResetReplaceDataAndClearPatches: function () {\n                var remoteModel = new Dirigible.GridRemoteModel(this.urls);\n                remoteModel.data = {'some':'stuff'};\n                remoteModel.patches = {0: {1: true}};\n                remoteModel.pending = {0: {1: true}};\n                var reset_to_this = {'some': 'new stuff'};\n\n                remoteModel.reset(reset_to_this);\n                \n                YAHOO.util.Assert.areSame(reset_to_this, remoteModel.data);\n                assertDeepAreSame({}, remoteModel.patches);\n                assertDeepAreSame({}, remoteModel.pending);\n            },\n\n            testGetLengthShouldReturnDataLength: function() {\n                var remoteModel = new Dirigible.GridRemoteModel(this.urls);\n                remoteModel.data = [1, 2, 3];\n                \n                YAHOO.util.Assert.areSame(3, remoteModel.getLength());\n            },\n\n            testGetItemShouldReturnDataItem: function() {\n                var remoteModel = new Dirigible.GridRemoteModel(this.urls);\n                remoteModel.data = [1, 2, 3];\n                \n                YAHOO.util.Assert.areSame(2, remoteModel.getItem(1));\n            },\n\n            testEnsureDataChoosesPatchesToLoadAndLoadsThemIfNecessay: function() {\n                var remoteModel = new Dirigible.GridRemoteModel(this.urls);\n                remoteModel.choosePatchesToLoad = function() {\n                    return [ 1, 2, 3 ];\n                }\n                var calls = [];\n                remoteModel.loadPatchIfNecessary = function(arg){\n                    calls.push(arg)\n                }\n\n                remoteModel.ensureData(1, 101, 51, 122);\n\n                assertDeepAreSame([1,2,3], calls);\n\n            },\n            \n            testEnsureDataDoesntPassGarbageToLoadPatchIfNecessary: function() {\n                var remoteModel = new Dirigible.GridRemoteModel(this.urls);\n                remoteModel.loadPatchIfNecessary = function (arg) {\n                    YAHOO.util.Assert.isFalse(\n                        arg.patchCol === undefined, \n                        'loadPatchIfNecessary args should have patchCol'\n                    );\n                    YAHOO.util.Assert.isFalse(\n                        arg.patchRow === undefined,\n                        'loadPatchIfNecessary args should have patchRow'\n                    );\n                };\n\n                remoteModel.ensureData(1, 101, 51, 122);\n            }\n\n        })\n\n    ];\n</script>\n\n\n<script src=\"yuirunner.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/tests/grid_view_test.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n<html>\n<head>\n    <script type=\"text/javascript\" src=\"/static/jquery/jquery-1.4.2.min.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/jquery/jquery-ui-1.8.10.custom.min.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/jquery/jquery.caret.1.02.min.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/dirigible/tests/test_utils.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/json/json2.js\"></script>\n    <script src=\"yuitest/yuitest-combo.js\"></script>\n    <script src=\"jsmock.js\"></script>\n\n    <script src=\"/static/dirigible/scripts/htmlescape.js\"></script>\n\n    <script src=\"/static/dirigible/scripts/grid_view.js\"></script>\n\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"logger.css\" />\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"testlogger.css\" />\n</head>\n\n<body>\n<div id=\"id_results\">Please wait</div>\n\n<div id=\"id_editor_container\"></div>\n<!-- required for testScrollListener -->\n<div class='slick-viewport' style=\"overflow: auto; width: 99px; height: 88px;\">\n<div style=\"width: 1000px; height: 1000px\"></div>\n<div id=\"id_buffering_message\"></div>\n</div>\n\n\n<script type=\"text/javascript\">\n\ntests = [\n\n    new YAHOO.tool.TestCase({\n\n        name: \"WithRestoreCursorDecoratorTest\",\n\n        setUp: function() {\n            var self = this;\n            $(\"#id_editor_container\").append(\"<input class='editor-text'></input>\");\n            $(\"#id_editor_container\").append(\"<input id='id_formula_bar'></input>\");\n            $(\"#id_editor_container\").append(\"<input id='id_other_thing'></input>\");\n            this.mockControl = new MockControl();\n            this.mockGrid = this.mockControl.createMock({\n                editActiveCell: function() {}\n            });\n            this.mockGrid.onScroll = {subscribe: function () {}};\n            this.mockRemoteModel = this.mockControl.createMock({\n            });\n            this.mockRemoteModel.onDataLoaded = {subscribe: function() {}};\n            this.mockRemoteModel.onDataLoading = {subscribe: function() {}};\n            this.rawData = {};\n            this.headerData = [];\n            this.origHeaderFn = Dirigible.GridView.jsonToSlickGridRowHeaders;\n            Dirigible.GridView.jsonToSlickGridRowHeaders = function() {\n                return self.headerData;\n            }\n            this.gridView = new Dirigible.GridView(this.mockGrid, this.mockRemoteModel);\n        },\n\n        tearDown: function() {\n            Dirigible.GridView.jsonToSlickGridRowHeaders = this.origHeaderFn;\n            $('input.editor-text').remove();\n            $('#id_formula_bar').remove();\n            $('#id_other_thing').remove();\n        },\n\n        testWithRestoreCursorShouldSaveAndRestoreEditorAndFormulaBarContentsIfTheyArePresent: function () {\n            var $editor = $('input.editor-text');\n            var $formulaBar = $('#id_formula_bar');\n\n            $editor.val('hello');\n\n            function wrapMe() {\n                $editor.remove();\n                $(\"#id_editor_container\").append(\"<input class='editor-text'></input>\");\n                $formulaBar.val('');\n            }\n            \n            this.gridView.with_restore_cursor(wrapMe)();\n\n            $editor = $('input.editor-text');\n            YAHOO.util.Assert.areSame('hello', $editor.val());\n            YAHOO.util.Assert.areSame('hello', $formulaBar.val());\n        },\n\n        testWithRestoreCursorShouldCallEditActiveCellIfEditorWasActive: function () {\n            this.gridView.ensureCurrentViewportData = function() {};\n            var $editor = $('input.editor-text');\n            $editor.focus();\n\n            function wrapMe() {\n                $editor.remove();\n                $(\"#id_editor_container\").append(\"<input class='editor-text'></input>\");\n            }\n            \n            this.mockGrid.expects().editActiveCell();\n\n            this.gridView.with_restore_cursor(wrapMe)();\n\n            this.mockControl.verify();\n        },\n\n        testWithRestoreCursorShouldCallEditActiveCellIfFormulaBarWasActive: function () {\n            this.gridView.ensureCurrentViewportData = function() {};\n            var $formulaBar = $('#id_formula_bar');\n            $formulaBar.focus();\n\n            function wrapMe() {\n                $('#id_other_thing').focus();\n            }\n            this.mockGrid.expects().editActiveCell();\n\n            this.gridView.with_restore_cursor(wrapMe)();\n\n            this.mockControl.verify();\n        },\n\n        testWithRestoreCursorShouldNotCallEditActiveCellIfEditorWasNotActive: function () {\n            this.gridView.ensureCurrentViewportData = function() {};\n            var $editor = $('input.editor-text');\n\n            function wrapMe() {\n                $editor.remove();\n                $(\"#id_editor_container\").append(\"<input class='editor-text'></input>\");\n            }\n\n            this.gridView.with_restore_cursor(wrapMe)();\n\n            this.mockControl.verify();\n        },\n\n        testWithRestoreCursorShouldPreserveFocusOnEditorIfActive: function () {\n            this.gridView.ensureCurrentViewportData = function() {};\n            var $editor = $('input.editor-text');\n            $editor.focus();\n\n            function wrapMe() {\n                $editor.remove();\n                $(\"#id_editor_container\").append(\"<input class='editor-text'></input>\");\n            }\n\n            this.gridView.with_restore_cursor(wrapMe)();\n\n            $editor = $('input.editor-text');\n            YAHOO.util.Assert.areSame(\n                    $editor[0],\n                    document.activeElement,\n                    'gridView.updateMetaData lost focus on editor box'\n            );\n        },\n\n        testWithResoreCursorDecoratorShouldPreserveCaretPositionAndSelectionInEditor: function () {\n            this.gridView.ensureCurrentViewportData = function() {};\n            var $editor = $('input.editor-text');\n            $editor.focus();\n            var caret_args = [];\n            $editor.extend($.fn, {\n                caret: function (start, end) {\n                    if (start === undefined && end === undefined) {\n                        caret_args.push('called with no args');\n                    } else{\n                        caret_args.push([start, end]);\n                    }\n                    return {start:123, end:456};\n                }\n            });\n\n            function wrapMe() {\n                $editor.remove();\n                $(\"#id_editor_container\").append(\"<input class='editor-text'></input>\");\n            }\n\n            this.gridView.with_restore_cursor(wrapMe)();\n\n            assertDeepAreSame(\n                ['called with no args', [123, 456]],\n                caret_args,\n                'caret function not called properly'\n            );\n\n        },\n\n        testWithRestoreCursorShouldPreserveCaretPositionAndSelectionInFormulaBar: function () {\n            this.gridView.ensureCurrentViewportData = function() {};\n            var $editor = $('input.editor-text');\n            var $formulaBar = $('#id_formula_bar');\n            $formulaBar.focus();\n            var caret_args = [];\n            $formulaBar.extend($.fn, {\n                caret: function (start, end) {\n                    if (start===undefined && end===undefined  ){\n                        caret_args.push('called with no args');\n                    } else{\n                        caret_args.push([start, end]);\n                    }\n                    return {start:123, end:456};\n                }\n            });\n\n            function wrapMe() {\n                $editor.remove();\n                $(\"#id_editor_container\").append(\"<input class='editor-text'></input>\");\n            }\n            \n            this.gridView.with_restore_cursor(wrapMe)();\n\n            assertDeepAreSame(\n                ['called with no args', [123, 456]],\n                caret_args,\n                'caret function not called properly'\n            );\n\n        },\n\n        testWithRestoreCursorShouldPreserveFocusOnFormulaBarIfActive: function () {\n            this.gridView.ensureCurrentViewportData = function() {};\n            var $formulaBar = $('#id_formula_bar');\n            $formulaBar.select();\n\n            function wrapMe() {\n                $('#id_other_thing').focus();\n            }\n\n            this.gridView.with_restore_cursor(wrapMe)();\n\n            YAHOO.util.Assert.areSame(\n                    $formulaBar[0],\n                    document.activeElement,\n                    'gridView.updateMetaData lost focus on formula bar'\n            );\n        },\n\n        testWithRestoreCursorShouldNotCrashIfNoEditorOrFormulaBarPresent: function () {\n            this.gridView.ensureCurrentViewportData = function() {};\n            // Remove the stuff that setUp created for us.\n            $('input.editor-text').remove();\n            $('#id_formula_bar').remove();\n\n            this.gridView.with_restore_cursor(function() {})();\n        }\n\n    }),\n\n\n    new YAHOO.tool.TestCase({\n\n        name: \"UpdateMetaDataTest\",\n\n        setUp: function() {\n            $(\"#id_editor_container\").append(\"<input class='editor-text'></input>\");\n            $(\"#id_editor_container\").append(\"<input id='id_formula_bar'></input>\");\n            $(\"#id_editor_container\").append(\"<input id='id_other_thing'></input>\");\n            this.mockControl = new MockControl();\n            this.mockGrid = this.mockControl.createMock({\n                editActiveCell: function() {},\n                getCellFromPoint: function() {},\n                render: function() {},\n                setColumns: function() {}\n            });\n            this.mockGrid.onScroll = {subscribe: function () {}};\n            this.mockRemoteModel = this.mockControl.createMock({\n                reset: function () {}\n            });\n            this.mockRemoteModel.onDataLoaded = {subscribe: function() {}};\n            this.mockRemoteModel.onDataLoading = {subscribe: function() {}};\n            this.rawData = {};\n            this.headerData = [];\n            this.origHeaderFn = Dirigible.GridView.jsonToSlickGridRowHeaders;\n            var that = this;\n            Dirigible.GridView.jsonToSlickGridRowHeaders = function() {\n                return that.headerData;\n            }\n            this.gridView = new Dirigible.GridView(this.mockGrid, this.mockRemoteModel);\n        },\n\n        tearDown: function() {\n            Dirigible.GridView.jsonToSlickGridRowHeaders = this.origHeaderFn;\n            $('input.editor-text').remove();\n            $('#id_formula_bar').remove();\n            $('#id_other_thing').remove();\n        },\n\n        testUpdateMetaDataShouldInvalidateRowsResetRemoteDataAndColumnsThenRender: function () {\n            var mockJsonToSlickGridRowHeadersOwner = this.mockControl.createMock({\n                jsonToSlickGridRowHeaders: function(){}\n            });\n            Dirigible.GridView.jsonToSlickGridRowHeaders = mockJsonToSlickGridRowHeadersOwner.jsonToSlickGridRowHeaders;\n\n            var mockEnsureCurrentViewportDataOwner = this.mockControl.createMock({\n                ensureCurrentViewportData: function(){}\n            });\n            this.gridView.ensureCurrentViewportData = mockEnsureCurrentViewportDataOwner.ensureCurrentViewportData;\n\n            mockJsonToSlickGridRowHeadersOwner.expects().jsonToSlickGridRowHeaders(this.rawData).andReturn(this.headerData);\n\n            this.mockRemoteModel.expects().reset(this.headerData);\n\n            this.mockGrid.expects().render();\n            mockEnsureCurrentViewportDataOwner.expects().ensureCurrentViewportData();\n\n            this.gridView.updateMetaData(this.rawData);\n\n            this.mockControl.verify();\n        },\n\n        testUpdateMetaDataIsWrappedWithWithRestoreCursor: function () {\n\n            YAHOO.util.Assert.isTrue(this.gridView.updateMetaData.wrapped);\n        }\n     \n    }),\n\n    new YAHOO.tool.TestCase({\n\n        name: \"HandleScrollTest\",\n\n        testInitialise: function () {\n            var mockControl = new MockControl();\n\n            var mockOnScroll = mockControl.createMock({\n                subscribe: function () {}\n            });\n            var mockGrid = {onScroll: mockOnScroll};\n\n            var mockRemoteModel = {\n                onDataLoaded: {subscribe: function() {}}, \n                onDataLoading: {subscribe: function() {}}\n            };\n\n            var onScrollSubscribeArgs = null;\n            mockOnScroll.expects().subscribe(TypeOf.isA(Function)).andStub(\n                function () {\n                    onScrollSubscribeArgs = arguments[0];\n                }\n            );\n\n            var gridView = new Dirigible.GridView(mockGrid, mockRemoteModel);\n\n            assertDeepAreSame(gridView.grid, mockGrid);\n            assertDeepAreSame(gridView.remoteModel, mockRemoteModel);\n            mockControl.verify();\n            assertDeepAreSame(gridView.ensureCurrentViewportData, onScrollSubscribeArgs);\n        }\n    }),\n\n\n    new YAHOO.tool.TestCase({\n\n        name: \"EnsureCurrentViewportDataTest\",\n\n        testEnsureCurrentViewportData: function() {\n            var mockControl = new MockControl();\n\n            var mockGrid = mockControl.createMock({\n                getCellFromPoint : function() {},\n            });\n            mockGrid.onScroll = {subscribe: function () {}};\n            \n            var mockRemoteModel = mockControl.createMock({\n                ensureData : function() {}\n            });\n            mockRemoteModel.onDataLoaded = {subscribe: function() {}};\n            mockRemoteModel.onDataLoading = {subscribe: function() {}};\n            \n            var gridView = new Dirigible.GridView(mockGrid, mockRemoteModel);\n\n            var event = null;\n            $('div.slick-viewport').scrollLeft(400);\n            $('div.slick-viewport').scrollTop(700);\n            $('div.slick-viewport').width(99);\n            $('div.slick-viewport').height(88);\n\n            mockGrid.expects().getCellFromPoint(\n                400, 700\n            ).andReturn({cell: 1, row: 2});\n            mockGrid.expects().getCellFromPoint(\n                499,\n                788\n            ).andReturn({cell: 3, row: 4});\n            mockRemoteModel.expects().ensureData(1, 2, 3, 4);\n\n            gridView.ensureCurrentViewportData();\n\n            mockControl.verify();\n        }\n    }),\n\n    new YAHOO.tool.TestCase({\n\n        name: \"HandleDataLoadEventsTest\",\n\n        setUp: function() {\n            this.mockControl = new MockControl();\n            this.mockGrid = this.mockControl.createMock({\n                invalidateRow: function() {},\n                render: function() {},\n                updateRowCount: function() {}\n            });\n            this.mockGrid.onScroll = {subscribe: function() {}};\n            this.mockRemoteModel = this.mockControl.createMock({\n                ensureData : function() {},\n                onDataLoaded: function() {},\n                onDataLoading: function() {}\n            });\n            this.mockRemoteModel.onDataLoaded.subscribe = function () {};\n            this.mockRemoteModel.onDataLoading.subscribe = function () {};\n            this.mockGrid.remoteModel = this.mockRemoteModel;\n        },\n\n        testInitialise: function () {\n            this.mockOnDataLoaded = this.mockControl.createMock({\n                subscribe: function () {}\n            });\n            this.mockOnDataLoading = this.mockControl.createMock({\n                subscribe: function () {}\n            });\n            this.mockRemoteModel.onDataLoaded = this.mockOnDataLoaded;\n            this.mockRemoteModel.onDataLoading = this.mockOnDataLoading;\n\n            var onDataLoadingSubscribeArgs = null;\n            this.mockRemoteModel.onDataLoading.expects().subscribe(TypeOf.isA(Function)).andStub(\n                function () {\n                    onDataLoadingSubscribeArgs = arguments[0];\n                }\n            );\n\n            var onDataLoadedSubscribeArgs = null;\n            this.mockRemoteModel.onDataLoaded.expects().subscribe(TypeOf.isA(Function)).andStub(\n                function () {\n                    onDataLoadedSubscribeArgs = arguments[0];\n                }\n            );\n\n            this.gridView = new Dirigible.GridView(this.mockGrid, this.mockRemoteModel);\n\n            this.mockControl.verify();\n            assertDeepAreSame(this.gridView.handleOnDataLoaded, onDataLoadedSubscribeArgs);\n            assertDeepAreSame(this.gridView.handleOnDataLoading, onDataLoadingSubscribeArgs);\n\n\n        },\n\n        testHandleOnDataLoaded: function() {\n            this.gridView = new Dirigible.GridView(this.mockGrid, this.mockRemoteModel);\n            var event = null;\n            var data = {topmost: 4, bottom: 7};\n\n            this.mockGrid.expects().invalidateRow(3);\n            this.mockGrid.expects().invalidateRow(4);\n            this.mockGrid.expects().invalidateRow(5);\n            this.mockGrid.expects().invalidateRow(6);\n            this.mockGrid.expects().updateRowCount();\n            this.mockGrid.expects().render();\n\n            this.gridView.handleOnDataLoaded(event, data);\n\n            this.mockControl.verify();\n\n            YAHOO.util.Assert.isTrue($('#id_buffering_message').hasClass('hidden'));\n        },\n\n        testHandleOnDataLoadedIsWrappedWithWithRestoreCursor: function () {\n            YAHOO.util.Assert.isTrue(this.gridView.handleOnDataLoaded.wrapped);\n        },\n\n        testHandleOnDataLoading: function() {\n            this.gridView = new Dirigible.GridView(this.mockGrid, this.mockRemoteModel);\n            YAHOO.util.Assert.isTrue($('#id_buffering_message').hasClass('hidden'));\n            this.gridView.handleOnDataLoading();\n            YAHOO.util.Assert.isFalse($('#id_buffering_message').hasClass('hidden'));\n        }\n    }),\n\n    new YAHOO.tool.TestCase({\n\n        name: \"JSONToSlickgridDataTest\",\n\n        testZeroSize : function () {\n            input = {\n                width: 0,\n                height: 0\n            };\n            expected = [];\n            assertDeepAreSame(\n                expected,\n                Dirigible.GridView.jsonToSlickGridData(input));\n        },\n\n        testTenByFiveEmpty : function () {\n            input = {\n                width: 10,\n                height: 5\n            };\n            expected = [\n                { header: 1 },\n                { header: 2 },\n                { header: 3 },\n                { header: 4 },\n                { header: 5 }\n            ];\n            assertDeepAreSame(\n                expected,\n                Dirigible.GridView.jsonToSlickGridData(input));\n        },\n\n        testTenByFiveWithContent: function () {\n            input = {\n                width: 10,\n                height: 5,\n\n                1 : {\n                    2 : { formula: \"Row 1, col 2 formula\", formatted_value: \"Row 1, col 2 value\" },\n                    10 : { formula: \"Row 1, col 10 formula\", formatted_value: \"Row 1, col 10 value\" }\n                },\n                5 : {\n                    1 : { formula: \"Row 5, col 1 formula\", formatted_value: \"Row 5, col 1 value\" },\n                    10 : { formula: \"Row 5, col 10 formula\", formatted_value: \"Row 5, col 10 value\" }\n                }\n            };\n            expected = [\n                {\n                    header: 1,\n                    2: { formula: \"Row 1, col 2 formula\", formatted_value: \"Row 1, col 2 value\" },\n                    10: { formula: \"Row 1, col 10 formula\", formatted_value: \"Row 1, col 10 value\" }\n                },\n                { header: 2 },\n                { header: 3 },\n                { header: 4 },\n                {\n                    header: 5,\n                    1: { formula: \"Row 5, col 1 formula\", formatted_value: \"Row 5, col 1 value\" },\n                    10: { formula: \"Row 5, col 10 formula\", formatted_value: \"Row 5, col 10 value\" }\n                }\n            ];\n            assertDeepAreSame(\n                expected,\n                Dirigible.GridView.jsonToSlickGridData(input)\n            );\n        }\n    }),\n\n    new YAHOO.tool.TestCase({\n\n        name: \"JSONToSlickGridHeadersTest\",\n\n        testColumnsFromGrid : function () {\n            var data =  {\"width\": 3, \"height\": 4, \"name\": \"Sheet 322\"};\n            var actual_result = Dirigible.GridView.jsonToSlickGridColumnHeaders(data);\n            var expected_result = [\n                { \"name\" : \"\", \"id\" : \"header\", \"field\" : \"header\", \"width\":40, \"sortable\" : false, unselectable: true },\n                { \"name\" : \"A\", \"id\" : \"1\", \"field\" : \"1\", \"formatter\" : Dirigible.GridView.cellFormatter },\n                { \"name\" : \"B\", \"id\" : \"2\", \"field\" : \"2\", \"formatter\" : Dirigible.GridView.cellFormatter },\n                { \"name\" : \"C\", \"id\" : \"3\", \"field\" : \"3\", \"formatter\" : Dirigible.GridView.cellFormatter }\n            ];\n            assertDeepAreSame(expected_result, actual_result);\n        },\n\n        testRowsFromGrid : function () {\n            var data =  {\"width\": 3, \"height\": 4, \"name\": \"Sheet 322\"};\n            var actual_result = Dirigible.GridView.jsonToSlickGridRowHeaders(data);\n            var expected_result = [\n                { \"header\" : 1 },\n                { \"header\" : 2 },\n                { \"header\" : 3 },\n                { \"header\" : 4 }\n            ];\n            assertDeepAreSame(expected_result, actual_result);\n        }\n    }),\n\n    new YAHOO.tool.TestCase({\n\n        name: \"ColumnIndexToNameTest\",\n\n        testColumnIndexToName: function () {\n            YAHOO.util.Assert.areSame('A', Dirigible.GridView.columnIndexToName(1));\n            YAHOO.util.Assert.areSame('Z', Dirigible.GridView.columnIndexToName(26));\n            YAHOO.util.Assert.areSame('AA', Dirigible.GridView.columnIndexToName(27));\n            YAHOO.util.Assert.areSame('AZ', Dirigible.GridView.columnIndexToName(52));\n        }\n\n    }),\n\n    new YAHOO.tool.TestCase({\n\n        name: \"CellFormatterTest\",\n\n        getExpectedSpan : function (value, forFormula) {\n            var cssClass = forFormula ? 'grid_formula' : 'grid_formatted_value';\n            return (\n                '<span class=\"' + cssClass + '\" title=\"' + value + '\">' +\n                value + '</span>'\n            );\n        },\n\n        testUndefinedObject : function () {\n            YAHOO.util.Assert.areSame(\n                \"\",\n                Dirigible.GridView.cellFormatter(null, null, undefined, null, null)\n            );\n        },\n\n        testEmptyObject : function () {\n            YAHOO.util.Assert.areSame(\n                \"\",\n                Dirigible.GridView.cellFormatter(null, null, {}, null, null)\n            );\n        },\n\n        testObjectWithValueThatNeedsEscaping : function () {\n            YAHOO.util.Assert.areSame(\n                this.getExpectedSpan(\"&lt;object&gt;\"),\n                Dirigible.GridView.cellFormatter(null, null, { formatted_value: \"<object>\" }, null, null)\n            );\n        },\n\n        testObjectWithFormula : function () {\n            YAHOO.util.Assert.areSame(\n                this.getExpectedSpan(\"=1 &lt; 3\", true),\n                Dirigible.GridView.cellFormatter(null, null, { formula: \"=1 < 3\" }, null, null)\n            );\n        },\n\n        testObjectWithError : function () {\n            var column = 100;\n            var row = 2;\n            var escaped_error = 'fish &amp; chips &gt; spam &lt; this &quot;parrot&quot;';\n            var expected = \"<img id='id_\" + column + \"_\" + (row + 1) + \"_error' title='\" + escaped_error + \"' src='/static/dirigible/images/error.gif' />\";\n            YAHOO.util.Assert.areSame(\n                expected,\n                Dirigible.GridView.cellFormatter(row, column, { error: 'fish & chips > spam < this \"parrot\"'}, null, null)\n            );\n        },\n\n        testObjectWithValueAndFormula : function () {\n            YAHOO.util.Assert.areSame(\n                this.getExpectedSpan('123'),\n                Dirigible.GridView.cellFormatter(null, null, { formatted_value: \"123\", formula: \"=goo\" }, null, null)\n            );\n        },\n\n        testObjectWithEverything : function () {\n            var expected = \"<img id='id_3_5_error' title='hello world' src='/static/dirigible/images/error.gif' />\";\n            YAHOO.util.Assert.areSame(\n                expected,\n                Dirigible.GridView.cellFormatter(4, 3, { formatted_value: \"123\", formula: \"=goo\", error: \"hello world\" }, null, null)\n            );\n        }\n\n    })\n\n];\n</script>\n\n\n<script src=\"yuirunner.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/tests/htmlescape_test.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n<html>\n<head>\n    <script type=\"text/javascript\" src=\"/static/jquery/jquery-1.4.2.min.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/dirigible/tests/test_utils.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/json/json2.js\"></script>\n    <script src=\"yuitest/yuitest-combo.js\"></script>\n    <script src=\"jsmock.js\"></script>\n\n    <script src=\"../scripts/htmlescape.js\"></script>\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"logger.css\" />\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"testlogger.css\" />\n</head>\n\n<body>\n<div id=\"id_results\">Please wait</div>\n\n\n<script type=\"text/javascript\">\n\n    tests = [\n\n        new YAHOO.tool.TestCase({\n\n            name: \"testEscape\",\n\n            testEscape: function () {\n\n                YAHOO.util.Assert.areSame('&lt;', htmlescape('<') );\n                YAHOO.util.Assert.areSame('&gt;', htmlescape('>') );\n                YAHOO.util.Assert.areSame('&amp;', htmlescape('&') );\n                YAHOO.util.Assert.areSame('&quot;', htmlescape('\"') );\n                YAHOO.util.Assert.areSame('&#39;', htmlescape(\"'\") );\n\n                YAHOO.util.Assert.areSame(\n                    'fish &amp; chips &gt; &quot;spam&quot; &lt; &#39;this parrot&#39;',\n                    htmlescape('fish & chips > \"spam\" < \\'this parrot\\'')\n                );\n            }\n\n        })\n\n    ];\n</script>\n\n<div id=\"id_editor_container\"></div>\n\n<script src=\"yuirunner.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/tests/jsmock.js",
    "content": "/*\r\n*   JSMock 1.2.2, a mock object library for JavaScript\r\n* Copyright (C) 2006 Justin DeWind\r\n*\r\n* This library is free software; you can redistribute it and/or\r\n* modify it under the terms of the GNU Lesser General Public\r\n* License as published by the Free Software Foundation; either\r\n* version 2.1 of the License, or (at your option) any later version.\r\n*\r\n* This library is distributed in the hope that it will be useful,\r\n* but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r\n* Lesser General Public License for more details.\r\n*\r\n* You should have received a copy of the GNU Lesser General Public\r\n* License along with this library; if not, write to the Free Software\r\n* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA\r\n*/\r\n\r\nJSMock = {\r\n  extend: function(object) {\r\n    var mockControl = new MockControl();\r\n    object.createMock = function(objectToMock) {return mockControl.createMock(objectToMock)};\r\n    object.resetMocks = function() {mockControl.reset()};\r\n    object.verifyMocks = function() {mockControl.verify()};\r\n\r\n    if(!object.tearDown) {\r\n      object.tearDown = function() {\r\n        object.verifyMocks();\r\n      }\r\n    }\r\n    else if(object.tearDown.constructor == Function) {\r\n      object.__oldTearDown__ = object.tearDown;\r\n      object.tearDown = function() {\r\n        object.__oldTearDown__();\r\n        object.verifyMocks();\r\n      }\r\n    }\r\n  }\r\n}\r\n\r\nfunction MockControl() {\r\n    this.__expectationMatcher = new ExpectationMatcher();\r\n    this.__lastMock = null;\r\n    this.__lastCallName = null;\r\n}\r\n\r\nMockControl.prototype = {\r\n\r\n    createMock: function(objectToMock) {\r\n        var mock = { calls: [], expects: function() {this.__recording = true; return this}, __recording: false};\r\n        mock.expect = mock.expects;\r\n\r\n        if(objectToMock != null) {\r\n\r\n            if( typeof(objectToMock) == 'function' ) {\r\n                this.__createMethods(objectToMock, mock);\r\n                this.__createMethods(new objectToMock(), mock);\r\n            }\r\n            else if( typeof(objectToMock) == 'object') {\r\n                this.__createMethods(objectToMock, mock);\r\n            }\r\n            else {\r\n                throw new Error(\"Cannot mock out a \" + typeof(objectToMock));\r\n            }\r\n\r\n        }\r\n\r\n        var self = this;\r\n        mock.addMockMethod = function(method) { self.__createMethod(self, mock, method); }\r\n\r\n        return mock;\r\n    },\r\n\r\n    andReturn: function(returnValue) {\r\n        this.__verifyLastMockNotNull(\"Cannot set return value without an expectation\");\r\n        this.__initializeReturnExpectationForMock();\r\n        this.__lastMock.calls[this.__lastCallName].push( function() { return returnValue; });\r\n    },\r\n\r\n    andThrow: function(throwMsg) {\r\n        this.__verifyLastMockNotNull(\"Cannot throw error without an expectation\");\r\n        this.__initializeReturnExpectationForMock();\r\n        this.__lastMock.calls[this.__lastCallName].push( function() { throw new Error(throwMsg); });\r\n    },\r\n\r\n    andStub: function(block) {\r\n        this.__verifyLastMockNotNull(\"Cannot stub without an expectation\");\r\n        if( typeof(block) != 'function') {\r\n            throw new Error(\"Stub must be a function\");\r\n        }\r\n        this.__initializeReturnExpectationForMock();\r\n        this.__lastMock.calls[this.__lastCallName].push( function() { return block.apply(this, arguments); });\r\n    },\r\n\r\n    reset: function() {\r\n      this.__expectationMatcher.reset();\r\n    },\r\n\r\n    verify: function() {\r\n        if(!this.__expectationMatcher.matches())\r\n        {\r\n            discrepancy = this.__expectationMatcher.discrepancy();\r\n            message = discrepancy.message;\r\n            method = discrepancy.behavior.method\r\n            formattedArgs = ArgumentFormatter.format(discrepancy.behavior.methodArguments);\r\n            this.__expectationMatcher.reset();\r\n            throw new Error(message + \": \" + method + \"(\" + formattedArgs + \")\");\r\n        }\r\n        else {\r\n          this.__expectationMatcher.reset();\r\n        }\r\n\r\n    },\r\n\r\n    __createMethods: function(object, mock) {\r\n        for( property in object )   {\r\n            if( this.__isPublicMethod(object, property) ) {\r\n                this.__createMethod( this, mock, property );\r\n            }\r\n        }\r\n    },\r\n\r\n    __createMethod: function(control, mock, method) {\r\n        mock[method] =\r\n            function() {\r\n                if( mock.__recording ) {\r\n                        control.__lastMock = mock;\r\n                        control.__lastCallName = method;\r\n                        control.__expectationMatcher.addExpectedMethodCall( mock, method, arguments );\r\n                        mock.__recording = false;\r\n                        return control;\r\n                    }\r\n                    else {\r\n                        control.__expectationMatcher.addActualMethodCall( mock, method, arguments );\r\n                        if( mock.calls[method] != null) {\r\n                            returnValue = mock.calls[method].shift();\r\n                            if( typeof(returnValue) == 'function') {\r\n                                return returnValue.apply(this, arguments);\r\n                            }\r\n                        }\r\n                    }\r\n            }\r\n    },\r\n\r\n    __isPublicMethod: function(object, property) {\r\n        try {\r\n            return typeof(object[property]) == 'function' && property.charAt(0) != \"_\";\r\n        } catch(e) {\r\n            return false;\r\n        }\r\n    },\r\n\r\n    __verifyLastMockNotNull: function(throwMsg) {\r\n        if(this.__lastMock == null) {\r\n            throw new Error(throwMsg);\r\n        }\r\n    },\r\n\r\n    __initializeReturnExpectationForMock: function() {\r\n        if(typeof(this.__lastMock.calls[this.__lastCallName]) == 'undefined') {\r\n            this.__lastMock.calls[this.__lastCallName] = [];\r\n        }\r\n    }\r\n}\r\n\r\nfunction ExpectationMatcher() {\r\n    this.__expectationBehaviorList = [];\r\n    this.__actualBehaviorList = [];\r\n    this.__discrepancy = null;\r\n\r\n}\r\n\r\nExpectationMatcher.prototype = {\r\n    addExpectedMethodCall: function(caller, method, methodArguments ) {\r\n        this.__expectationBehaviorList.push(new InvocationBehavior(caller, method, methodArguments));\r\n    },\r\n\r\n    addActualMethodCall: function(caller, method, methodArguments ) {\r\n        this.__actualBehaviorList.push(new InvocationBehavior(caller, method, methodArguments));\r\n    },\r\n\r\n    matches: function() {\r\n      var self = this;\r\n      var matches = true;\r\n\r\n      this.__expectationBehaviorList.eachIndexForJsMock(function(index, expectedBehavior) {\r\n        var actualBehavior = (self.__actualBehaviorList.length > index) ? self.__actualBehaviorList[index] : null;\r\n\r\n        if(matches) {\r\n          if( actualBehavior === null ) {\r\n                self.__discrepancy = new Discrepancy(\"Expected function not called\", expectedBehavior);\r\n                matches = false;\r\n            }\r\n            else if( expectedBehavior.method != actualBehavior.method ) {\r\n                self.__discrepancy = new Discrepancy(\"Surprise call\", actualBehavior);\r\n                matches = false;\r\n            }\r\n            else if( expectedBehavior.caller != actualBehavior.caller ) {\r\n                self.__discrepancy = new Discrepancy(\"Surprise call from unexpected caller\", actualBehavior);\r\n                matches = false;\r\n            }\r\n            else if( !self.__matchArguments(expectedBehavior.methodArguments, actualBehavior.methodArguments) ) {\r\n                self.__discrepancy = new Discrepancy(\"Unexpected Arguments\", actualBehavior);\r\n                matches = false;\r\n            }\r\n        }\r\n      });\r\n\r\n        if( this.__actualBehaviorList.length > this.__expectationBehaviorList.length && matches ) {\r\n            this.__discrepancy = new Discrepancy(\"Surprise call\", this.__actualBehaviorList[this.__expectationBehaviorList.length]);\r\n            matches = false\r\n        }\r\n\r\n        return matches;\r\n    },\r\n\r\n    reset: function() {\r\n        this.__expectationBehaviorList = [];\r\n        this.__actualBehaviorList = [];\r\n        this.__discrepancy = null;\r\n    },\r\n\r\n    discrepancy: function() {\r\n        return this.__discrepancy;\r\n    },\r\n\r\n    __matchArguments: function(expectedArgs, actualArgs) {\r\n        var expectedArray = this.__convertArgumentsToArray(expectedArgs);\r\n        var actualArray = this.__convertArgumentsToArray(actualArgs);\r\n        return ArgumentMatcher.matches(expectedArray, actualArray);\r\n    },\r\n\r\n    __convertArgumentsToArray: function(args) {\r\n        var convertedArguments = [];\r\n\r\n        for(var i = 0; i < args.length; i++) {\r\n            convertedArguments[i] = args[i];\r\n        }\r\n\r\n        return convertedArguments;\r\n    }\r\n}\r\n\r\nfunction InvocationBehavior(caller, method, methodArguments) {\r\n    this.caller = caller;\r\n    this.method = method;\r\n    this.methodArguments = methodArguments;\r\n}\r\n\r\nfunction TypeOf(type) {\r\n    if(typeof(type) != 'function')\r\n        throw new Error(\"Can only take constructors\");\r\n\r\n    this.type = type;\r\n}\r\n\r\nTypeOf.isA = function(type) { return new TypeOf(type); };\r\n\r\nArgumentMatcher = {\r\n\r\n    matches: function(expected, actual) {\r\n        return this.__delegateMatching(expected, actual);\r\n    },\r\n\r\n    __delegateMatching: function(expected, actual) {\r\n        if( expected == null ) {\r\n            return this.__match( expected, actual );\r\n        }\r\n        else if( expected.constructor == TypeOf ) {\r\n            return this.__match(expected.type, actual.constructor);\r\n        }\r\n        else if( expected.constructor == Array ) {\r\n            return this.__matchArrays(expected, actual);\r\n        }\r\n        else {\r\n            return this.__match(expected, actual);\r\n        }\r\n    },\r\n\r\n    __match: function(expected, actual) {\r\n        return ( expected == actual );\r\n    },\r\n\r\n    __matchArrays: function(expected, actual) {\r\n        if ( actual == null)\r\n            return false;\r\n\r\n        if( actual.constructor != Array)\r\n            return false;\r\n\r\n        if( expected.length != actual.length )\r\n            return false;\r\n\r\n        for(var i = 0; i < expected.length; i++ ) {\r\n            if( !this.__delegateMatching(expected[i], actual[i]) )\r\n                return false;\r\n        }\r\n\r\n        return true;\r\n    }\r\n}\r\n\r\nfunction Discrepancy(message, behavior) {\r\n    if(behavior.constructor != InvocationBehavior)\r\n        throw new Error(\"The behavior can only be an InvocationBehavior object\");\r\n\r\n    this.message = message;\r\n    this.behavior = behavior;\r\n}\r\n\r\nArgumentFormatter = {\r\n\r\n    format: function(args) {\r\n        var formattedArgs = \"\";\r\n        for(var i = 0; i < args.length; i++) {\r\n            if( args[i] == null ) {\r\n                formattedArgs += ( formattedArgs == \"\" ) ? \"null\" : \", \" + \"null\";\r\n            }\r\n            else if( args[i].constructor == TypeOf || args[i].constructor == Function) {\r\n                var func = ( args[i].constructor == TypeOf ) ? args[i].type : args[i];\r\n                formattedArgs += ( formattedArgs == \"\" ) ? this.__formatFunction(func) : \", \" + this.__formatFunction(func);\r\n            }\r\n            else if( typeof(args[i]) == \"string\" ) {\r\n                formattedArgs += ( formattedArgs == \"\" ) ? \"\\\"\" + args[i].toString() + \"\\\"\" : \", \\\"\" + args[i].toString() + \"\\\"\"\r\n            }\r\n            else if( args[i].constructor == Array ) {\r\n                formattedArgs += ( formattedArgs == \"\" ) ? \"[\" + this.format(args[i]) + \"]\" : \", [\" + this.format(args[i]) + \"]\";\r\n            }\r\n            else {\r\n                formattedArgs += ( formattedArgs == \"\" ) ? args[i].toString() : \", \" + args[i].toString();\r\n            }\r\n        }\r\n        return formattedArgs;\r\n    },\r\n\r\n    __formatFunction: function(func) {\r\n        // Manual checking is done for internal/native functions\r\n        // since Safari will not display them correctly\r\n        // for the intended regex parsing.\r\n\r\n        if(func == Array) {\r\n            return \"Array\";\r\n        } else if(func == Date) {\r\n            return \"Date\";\r\n        } else if(func == Object) {\r\n            return \"Object\";\r\n        } else if(func == String) {\r\n            return \"String\";\r\n        } else if(func == Function) {\r\n            return \"Function\";\r\n        } else if(func == RegExp) {\r\n            return \"RegExp\";\r\n        } else if(func == Error) {\r\n            return \"Error\";\r\n        } else if(func == Number) {\r\n            return \"Number\";\r\n        } else if(func == Boolean) {\r\n            return \"Boolean\";\r\n        }\r\n        var formattedFunc = func.toString().match(/function (\\w+)/);\r\n\r\n        return ( formattedFunc ==  null ) ? \"{{Closure}}\" : formattedFunc[1];\r\n    }\r\n\r\n}\r\n\r\n/* Helpers */\r\n\r\n// Implemented each method with a unique name to avoid conflicting\r\n// with other libraries that implement it.\r\nArray.prototype.eachIndexForJsMock = function(block) {\r\n  for(var index = 0; index < this.length; index++)\r\n  {\r\n    block(index, this[index]);\r\n  }\r\n}\r\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/tests/logger.css",
    "content": "/*\nCopyright (c) 2008, Yahoo! Inc. All rights reserved.\nCode licensed under the BSD License:\nhttp://developer.yahoo.net/yui/license.txt\nversion: 2.6.0\n*/\n/* logger default styles */\r\n/* default width: 31em */\r\n/* default font-size 77% */\r\n.yui-log {padding:1em;width:31em;background-color:#AAA;color:#000;border:1px solid black;font-family:monospace;font-size:77%;text-align:left;z-index:9000;}\r\n\r\n/* for containers built from scratch */\r\n.yui-log-container {position:absolute;top:1em;right:1em;}\r\n\r\n/* buttons */\r\n.yui-log input {\r\n    margin:0;padding:0;\r\n    font-family:arial;\r\n    font-size:100%;\r\n    font-weight:normal;\r\n}\r\n.yui-log .yui-log-btns {position:relative;float:right;bottom:.25em;}\r\n\r\n/* header */\r\n.yui-log .yui-log-hd {margin-top:1em;padding:.5em;background-color:#575757;}\r\n.yui-log .yui-log-hd h4 {margin:0;padding:0;font-size:107%;font-weight:bold;color:#FFF;}\r\n\r\n/* body */\r\n\r\n.yui-log .yui-log-bd {width:100%;height:20em;background-color:#FFF;border:1px solid gray;overflow:auto;} /* height is controlled here: default 20em*/\r\n.yui-log p {margin:1px;padding:.1em;}\r\n.yui-log pre {margin:0;padding:0;}\r\n\r\n/* for pre to respect newlines yet wrap long lines */\r\n/* http://www.longren.org/2006/09/27/wrapping-text-inside-pre-tags/ */\r\n.yui-log pre.yui-log-verbose {\r\n    white-space: pre-wrap; /* css-3 */\r\n    white-space: -moz-pre-wrap !important; /* Mozilla, since 1999 */\r\n    white-space: -pre-wrap; /* Opera 4-6 */\r\n    white-space: -o-pre-wrap; /* Opera 7 */\r\n    word-wrap: break-word; /* Internet Explorer 5.5+ */\r\n}\r\n\r\n/* footer */\r\n.yui-log .yui-log-ft {margin-top:.5em;}\r\n.yui-log .yui-log-ft .yui-log-categoryfilters {}\r\n.yui-log .yui-log-ft .yui-log-sourcefilters {width:100%;border-top:1px solid #575757;margin-top:.75em;padding-top:.75em;}\r\n.yui-log .yui-log-filtergrp {margin-right:.5em;}\r\n/*.yui-log .yui-log-ft ul {margin:0;padding:0;line-height:1.8}\r\n.yui-log .yui-log-ft li {list-style:none;display:inline;white-space:nowrap;}*/\r\n\r\n/* logs */\r\n.yui-log .info {background-color:#A7CC25;} /* A7CC25 green */\r\n.yui-log .warn {background-color:#F58516;} /* F58516 orange */\r\n.yui-log .error {background-color:#E32F0B;} /* E32F0B red */\r\n.yui-log .time {background-color:#A6C9D7;} /* A6C9D7 blue */\r\n.yui-log .window {background-color:#F2E886;} /* F2E886 tan */\r\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/tests/page_commands_test.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n<html>\n<head>\n    <script type=\"text/javascript\" src=\"/static/jquery/jquery-1.4.2.min.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/dirigible/tests/test_utils.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/json/json2.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/slickgrid/slick.core.js\"></script>\n    <script type=\"text/javascript\" src=\"yuitest/yuitest-combo.js\"></script>\n    <script type=\"text/javascript\" src=\"jsmock.js\"></script>\n\n    <script type=\"text/javascript\" src=\"../scripts/page_commands.js\"></script>\n\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"logger.css\" />\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"testlogger.css\" />\n</head>\n\n<body>\n<div id=\"id_results\">Please wait</div>\n\n\n<script type=\"text/javascript\">\n\ntests = [\n\n    new YAHOO.tool.TestCase({\n\n        name: \"testPageCommands\",\n\n        testRecalculateSavesUsercodeAndCommitsCurrentCellEditsAndQueuesRecalc: function () {\n            var mockControl = new MockControl();\n            Dirigible.SheetUtils = mockControl.createMock( {\n                abortOtherRecalculations: function () {},\n                queueRecalculation: function () {}\n            } );\n            var editorCommands = mockControl.createMock( {\n                saveUsercode: function () {}\n            } );\n            var gridCommands = mockControl.createMock( {\n                commitCurrentEdit: function () {}\n            } );\n\n            var pageCommands = new Dirigible.PageCommands(gridCommands, editorCommands);\n            editorCommands.expects().saveUsercode();\n            gridCommands.expects().commitCurrentEdit();\n            Dirigible.SheetUtils.expects().abortOtherRecalculations();\n            Dirigible.SheetUtils.expects().queueRecalculation();\n\n            pageCommands.recalculate();\n\n            mockControl.verify();\n        }\n\n    })\n\n];\n</script>\n\n\n<script src=\"yuirunner.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/tests/page_interaction_handler_test.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n<html>\n<head>\n    <script type=\"text/javascript\" src=\"/static/jquery/jquery-1.4.2.min.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/dirigible/tests/test_utils.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/json/json2.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/slickgrid/slick.core.js\"></script>\n    <script type=\"text/javascript\" src=\"yuitest/yuitest-combo.js\"></script>\n    <script type=\"text/javascript\" src=\"jsmock.js\"></script>\n\n    <script type=\"text/javascript\" src=\"../scripts/page_interaction_handler.js\"></script>\n\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"logger.css\" />\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"testlogger.css\" />\n</head>\n\n<body>\n<div id=\"id_results\">Please wait</div>\n\n<div id=\"id_sheet_name\">test sheet name</div>\n\n<div id=\"id_right_column\">\n</div>\n\n<div id=\"id_header_and_toolbar\">\n</div>\n\n\n<script type=\"text/javascript\">\n\ntests = [\n\n    new YAHOO.tool.TestCase({\n\n        name: \"testPageInteractionHandler\",\n\n        setUp: function() {\n            this.mockControl = new MockControl();\n            this.dummyEditor = {\n                getSession: function () {\n                    return {addEventListener: function(){}};\n                },\n                addEventListener: function () {}\n            };\n            this.editor = this.mockControl.createMock(this.dummyEditor);\n\n            this.mockSession = this.mockControl.createMock({\n                addEventListener: function () {}\n            });\n            this.editorCommands = this.mockControl.createMock({\n                saveUsercode: function () {}\n            });\n            this.gridCommands = this.mockControl.createMock({\n                commitCurrentEdit: function () {}\n            });\n            this.pageCommands = this.mockControl.createMock({\n                recalculate: function() {}\n            });\n            this.mockEditController = this.mockControl.createMock({\n                commitCurrentEdit: function() {}\n            });\n            this.mockGrid = this.mockControl.createMock({\n                getEditController: function() {}\n            });\n            this.mockGrid.onActiveCellChanged = this.mockControl.createMock({\n                notify: function() {}\n            });\n\n        },\n\n\n        tearDown: function () {\n            // IMPORTANT!  Without these, each unit test will bind a new set of event handlers\n            // to the DOM, which can lead to very confusing failures.\n            $('#id_right_column, #id_header_and_toolbar').unbind('click');\n            $(document).unbind(\"keydown\");\n            $(document).unbind(\"mouseup\");\n            $(window).unbind(\"resize\");\n        },\n\n\n        testBindShouldBindClickAwayHandlersToEditor: function () {\n            var pageIH = new Dirigible.PageInteractionHandler(\n                $, null, this.pageCommands, this.editor, this.editorCommands, null, this.gridCommands\n            );\n\n            this.editor.expects().addEventListener(\n                'click', pageIH.commitCurrentEdit);\n            this.editor.expects().addEventListener(\n                'blur', this.editorCommands.saveUsercode);\n            this.editor.expects().getSession().andReturn(\n                this.mockSession);\n            this.mockSession.expects().addEventListener(\n                'change', TypeOf.isA(Function))\n                .andStub(function() {\n                    changeHandler = arguments[1]\n            });\n\n            pageIH.bind();\n\n            this.mockControl.verify();\n\n            YAHOO.util.Assert.isFalse(this.editor.usercodeDirty);\n            changeHandler();\n            YAHOO.util.Assert.isTrue(this.editor.usercodeDirty);\n        },\n\n        testBindShouldBindClickAwayHandlersToOtherStuff: function () {\n            var pageIH = new Dirigible.PageInteractionHandler(\n                $, null, this.pageCommands, this.dummyEditor, this.editorCommands, null, this.gridCommands\n            );\n            var commitCurrentEditObject = this.mockControl.createMock({\n                commitCurrentEdit: function() {}\n            });\n            pageIH.commitCurrentEdit = commitCurrentEditObject.commitCurrentEdit;\n\n            pageIH.bind();\n\n            commitCurrentEditObject.expects().commitCurrentEdit(TypeOf.isA(Object));\n            commitCurrentEditObject.expects().commitCurrentEdit(TypeOf.isA(Object));\n\n            $('#id_right_column').trigger('click');\n            $('#id_header_and_toolbar').trigger('click');\n\n            this.mockControl.verify();\n        },\n\n        testBindShouldBindCtrlSOnPageToSaveUserCode: function () {\n            this.editorCommands.expects().saveUsercode();\n\n            var pageIH = new Dirigible.PageInteractionHandler(\n                $, null, this.pageCommands, this.dummyEditor, this.editorCommands, null, this.gridCommands\n            );\n            pageIH.bind();\n            var event = jQuery.Event(\"keydown\");\n            event.ctrlKey = true;\n            event.which = 83;\n            $('body').trigger(event);\n\n            this.mockControl.verify();\n\n            YAHOO.util.Assert.isTrue(event.isDefaultPrevented());\n        },\n\n        testBindShouldBindF9ToRecalculate: function () {\n            this.pageCommands.expects().recalculate();\n\n            var pageIH = new Dirigible.PageInteractionHandler(\n                $, null, this.pageCommands, this.dummyEditor, this.editorCommands, null, this.gridCommands\n            );\n            pageIH.bind();\n            var event = jQuery.Event(\"keydown\");\n            event.which = 120;\n            $('body').trigger(event);\n\n            this.mockControl.verify();\n\n            YAHOO.util.Assert.isTrue(event.isDefaultPrevented());\n        },\n\n        testBindShouldBindDocumentMouseUpToQueuedResizeComponents: function() {\n            var mockDocument = this.mockControl.createMock({\n                mouseup: function() {},\n                keydown: function() {}\n            });\n            var mockJQuery = function(locator) {\n                if (locator === document) {\n                    return mockDocument;\n                }\n                return $(locator);\n            };\n            var pageIH = new Dirigible.PageInteractionHandler(\n                mockJQuery, null, this.pageCommands, this.dummyEditor, this.editorCommands, null, this.gridCommands\n            );\n\n            mockDocument.expects().keydown(TypeOf.isA(Function));\n            var mouseUpParam;\n            mockDocument.expects().mouseup(\n                TypeOf.isA(Function)\n            ).andStub(\n                function() {\n                    mouseUpParam = arguments[0];\n                }\n            );\n\n            pageIH.bind();\n\n            this.mockControl.verify();\n            this.mockControl.reset();\n\n            var mockWindow = this.mockControl.createMock({\n                setTimeout: function() {}\n            });\n            var originalSetTimeout = window.setTimeout;\n            try {\n                window.setTimeout = mockWindow.setTimeout;\n                mockWindow.expects().setTimeout(pageIH.resizeComponents, 0);\n                mouseUpParam();\n                this.mockControl.verify();\n            } finally {\n                window.setTimeout = originalSetTimeout;\n            }\n        },\n\n        testBindShouldBindWindowResizeToResizeComponents: function() {\n            var mockWindow = this.mockControl.createMock({\n                resize: function() {},\n                setTimeout: function() {}\n            });\n            var mockJQuery = function(locator) {\n                if (locator === window) {\n                    return mockWindow;\n                }\n                return $(locator);\n            };\n           var mockGrid = this.mockControl.createMock({\n                resizeCanvas: function() {}\n            });\n            var pageIH = new Dirigible.PageInteractionHandler(\n                    mockJQuery, null, this.pageCommands, this.dummyEditor,\n                    this.editorCommands, mockGrid, this.gridCommands\n            );\n\n            var resizeParam;\n            mockWindow.expects().resize(\n                TypeOf.isA(Function)\n            ).andStub(\n                function() {\n                    resizeParam = arguments[0];\n                }\n            );\n\n            pageIH.bind();\n\n            this.mockControl.verify();\n            this.mockControl.reset();\n\n            var originalSetTimeout = window.setTimeout;\n            try {\n                mockWindow.expects().setTimeout(pageIH.resizeComponents, 0);\n                window.setTimeout = mockWindow.setTimeout;\n                resizeParam();\n                this.mockControl.verify();\n            } finally {\n                window.setTimeout = originalSetTimeout;\n            }\n        },\n\n        testCommitCurrentEditShouldCallPageCommandsCommitCurrentEdit: function() {\n            var pageIH = new Dirigible.PageInteractionHandler(\n                $, null, this.pageCommands, this.dummyEditor, this.editorCommands, this.mockGrid, this.gridCommands\n            );\n            this.mockControl.reset();\n\n            this.gridCommands.expects().commitCurrentEdit();\n\n            pageIH.commitCurrentEdit();\n\n            this.mockControl.verify();\n        },\n\n        testInitialiseComponentsShouldInitialiseSheetNameEditorIfEditable: function() {\n            var mockJQuery = this.mockControl.createMock({\n                find: function() {}\n            });\n\n            var mockPageView = {\n                updatePageTitle: {}\n            };\n\n            var pageIH = new Dirigible.PageInteractionHandler(\n                mockJQuery.find, mockPageView, this.pageCommands, this.dummyEditor, this.editorCommands, this.mockGrid, this.gridCommands\n            );\n\n            var mockSheetName = this.mockControl.createMock({\n                eip: function() {}\n            });\n            mockJQuery.expects().find('#id_sheet_name').andReturn(mockSheetName);\n            var eipParams;\n            mockSheetName.expects().eip(\n                'set sheet name url',\n                TypeOf.isA(Object)\n            ).andStub(\n                function() {eipParams = arguments[1];}\n            );\n\n            var mockSplittable = {\n                splitter: function() {}\n            };\n            mockJQuery.expects().find('#id_grid_and_code').andReturn(mockSplittable);\n            mockJQuery.expects().find('#id_right_column').andReturn(mockSplittable);\n\n            pageIH.initialiseComponents({setSheetName: 'set sheet name url'}, true);\n\n            this.mockControl.verify();\n            assertDeepAreSame(\n                {\n                    select_text : true,\n                    form_buttons : '',\n                    text_form: '<input type=\"text\" id=\"edit-#{id}\" class=\"#{editfield_class}\" value=\"#{value}\" /> ',\n                    after_save: mockPageView.updatePageTitle\n                },\n                eipParams\n            );\n        },\n\n        testInitialiseComponentsShouldNotInitialiseSheetNameEditorIfNotEditable: function() {\n            var mockJQuery = this.mockControl.createMock({\n                find: function() {}\n            });\n\n            var mockPageView = {\n                updatePageTitle: {}\n            };\n\n            var pageIH = new Dirigible.PageInteractionHandler(\n                mockJQuery.find, mockPageView, this.pageCommands, this.dummyEditor, this.editorCommands, this.mockGrid, this.gridCommands\n            );\n\n            var mockSheetName = this.mockControl.createMock({\n                eip: function() {}\n            });\n\n            var mockSplittable = {\n                splitter: function() {}\n            };\n            mockJQuery.expects().find('#id_grid_and_code').andReturn(mockSplittable);\n            mockJQuery.expects().find('#id_right_column').andReturn(mockSplittable);\n\n            pageIH.initialiseComponents({setSheetName: 'set sheet name url'}, false);\n\n            this.mockControl.verify();\n        },\n\n        testInitialiseComponentsShouldInitialiseSplitters: function() {\n            var mockJQuery = this.mockControl.createMock({\n                find: function() {}\n            });\n\n            var pageIH = new Dirigible.PageInteractionHandler(\n                mockJQuery.find, {}, this.pageCommands, this.dummyEditor, this.editorCommands, this.mockGrid, this.gridCommands\n            );\n\n            var mockGridAndCodeDiv = this.mockControl.createMock({\n                splitter: function() {}\n            });\n            mockJQuery.expects().find('#id_grid_and_code').andReturn(mockGridAndCodeDiv);\n            var vSplitterParams;\n            mockGridAndCodeDiv.expects().splitter(\n                TypeOf.isA(Object)\n            ).andStub(\n                function() {vSplitterParams = arguments[0];}\n            );\n\n            var mockRightColumnDiv = this.mockControl.createMock({\n                splitter: function() {}\n            });\n            mockJQuery.expects().find('#id_right_column').andReturn(mockRightColumnDiv);\n            var hSplitterParams;\n            mockRightColumnDiv.expects().splitter(\n                TypeOf.isA(Object)\n            ).andStub(\n                function() {hSplitterParams = arguments[0];}\n            );\n\n            pageIH.initialiseComponents({}, false);\n\n            this.mockControl.verify();\n            assertDeepAreSame(\n                {\n                    splitVertical: true,\n                    outline: true,\n                    anchorToWindow: true,\n                    sizeLeft: true,\n                    cookie: 'dirigible_vertical_splitter'\n                },\n                vSplitterParams\n            );\n            assertDeepAreSame(\n                {\n                    splitHorizontal: true,\n                    outline: true,\n                    anchorToWindow: true,\n                    sizeBottom: true,\n                    cookie: 'dirigible_horizontal_splitter'\n                },\n                hSplitterParams\n            );\n        },\n\n        testResizeComponentsShouldResizeConsole: function() {\n            var dummyEditor = {\n                resize: function() {}\n            };\n            var dummyGrid = {\n                resizeCanvas: function() {}\n            };\n            var dummyComponent = {\n                width: function() {},\n                height: function() {},\n                show: function() {},\n                hide: function() {}\n            };\n            var mockConsole = this.mockControl.createMock(dummyComponent);\n            var mockRightColumn = this.mockControl.createMock(dummyComponent);\n            var mockConsoleWrap = this.mockControl.createMock(dummyComponent);\n            var mockJQuery = function(selector) {\n                if (selector === '#id_console') {\n                    return mockConsole;\n                } else if (selector === '#id_right_column') {\n                    return mockRightColumn;\n                } else if (selector === '#id_console_wrap') {\n                    return mockConsoleWrap;\n                }\n                return dummyComponent;\n            }\n\n            mockRightColumn.expects().width().andReturn(100);\n            mockConsole.expects().width(80);\n            mockConsoleWrap.expects().height().andReturn(110);\n            mockConsole.expects().height(90);\n\n            var pageIH = new Dirigible.PageInteractionHandler(\n                mockJQuery, null, this.pageCommands, dummyEditor, null, dummyGrid, null\n            );\n            pageIH.resizeComponents();\n\n            this.mockControl.verify();\n        },\n\n        testResizeComponentsShouldResizeGrid: function() {\n            var dummyEditor = {\n                resize: function() {}\n            };\n            var mockGrid = this.mockControl.createMock({\n                resizeCanvas: function() {}\n            });\n            var dummyComponent = {\n                width: function() {},\n                height: function() {},\n                show: function() {},\n                hide: function() {}\n            };\n            var mockGridAndFormulaBar = this.mockControl.createMock(dummyComponent);\n            var mockGridDiv = this.mockControl.createMock(dummyComponent);\n            var mockLeftColumn = this.mockControl.createMock(dummyComponent);\n            var mockFormulaBar = this.mockControl.createMock(dummyComponent);\n\n            var mockJQuery = function(selector) {\n                if (selector === '#id_grid_and_formula_bar') {\n                    return mockGridAndFormulaBar;\n                } else if (selector === '#id_grid') {\n                    return mockGridDiv;\n                } else if (selector === '#id_left_column') {\n                    return mockLeftColumn;\n                } else if (selector === '#id_formula_bar') {\n                    return mockFormulaBar;\n                }\n                return dummyComponent;\n            }\n\n            mockLeftColumn.expects().height().andReturn(100);\n            mockGridAndFormulaBar.expects().height(90);\n            mockFormulaBar.expects().height().andReturn(30);\n            mockGridDiv.expects().height(42);\n            mockGridDiv.expects().width().andReturn(110);\n            mockFormulaBar.expects().width(102);\n            mockGrid.expects().resizeCanvas();\n\n            var pageIH = new Dirigible.PageInteractionHandler(\n                mockJQuery, null, this.pageCommands, dummyEditor, null, mockGrid, null\n            );\n            pageIH.resizeComponents();\n\n            this.mockControl.verify();\n        },\n\n        testResizeComponentsShouldResizeUserCodeAndShowEditorIfHeightIsSufficient: function() {\n            var mockEditor = this.mockControl.createMock({\n                resize: function() {}\n            });\n            var dummyGrid = {\n                resizeCanvas: function() {}\n            };\n            var dummyComponent = {\n                width: function() {},\n                height: function() {},\n                show: function() {},\n                hide: function() {}\n            };\n\n            var mockUsercodeWrap = this.mockControl.createMock(dummyComponent);\n            var mockUsercodeDiv = this.mockControl.createMock(dummyComponent);\n\n            var mockJQuery = function(selector) {\n                if (selector === '#id_usercode_wrap') {\n                    return mockUsercodeWrap;\n                } else if (selector === '#id_usercode') {\n                    return mockUsercodeDiv;\n                }\n                return dummyComponent;\n            }\n\n            mockUsercodeWrap.expects().height().andReturn(90);\n            mockUsercodeDiv.expects().show();\n            mockUsercodeWrap.expects().width().andReturn(110);\n            mockUsercodeDiv.expects().width(90);\n            mockUsercodeWrap.expects().height().andReturn(200);\n            mockUsercodeDiv.expects().height(190);\n            mockEditor.expects().resize();\n\n            var pageIH = new Dirigible.PageInteractionHandler(\n                mockJQuery, null, this.pageCommands, mockEditor, null, dummyGrid, null\n            );\n            pageIH.resizeComponents();\n\n            this.mockControl.verify();\n        },\n\n        testResizeComponentsShouldResizeUserCodeAndHideEditorIfHeightIsInsufficient: function() {\n            var mockEditor = this.mockControl.createMock({\n                resize: function() {}\n            });\n            var dummyGrid = {\n                resizeCanvas: function() {}\n            };\n            var dummyComponent = {\n                width: function() {},\n                height: function() {},\n                show: function() {},\n                hide: function() {}\n            };\n\n            var mockUsercodeWrap = this.mockControl.createMock(dummyComponent);\n            var mockUsercodeDiv = this.mockControl.createMock(dummyComponent);\n\n            var mockJQuery = function(selector) {\n                if (selector === '#id_usercode_wrap') {\n                    return mockUsercodeWrap;\n                } else if (selector === '#id_usercode') {\n                    return mockUsercodeDiv;\n                }\n                return dummyComponent;\n            }\n\n            mockUsercodeWrap.expects().height().andReturn(29);\n            mockUsercodeDiv.expects().hide();\n            mockUsercodeWrap.expects().width().andReturn(110);\n            mockUsercodeDiv.expects().width(90);\n            mockUsercodeWrap.expects().height().andReturn(200);\n            mockUsercodeDiv.expects().height(190);\n            mockEditor.expects().resize();\n\n            var pageIH = new Dirigible.PageInteractionHandler(\n                mockJQuery, null, this.pageCommands, mockEditor, null, dummyGrid, null\n            );\n            pageIH.resizeComponents();\n\n            this.mockControl.verify();\n        }\n    })\n];\n</script>\n\n\n<script src=\"yuirunner.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/tests/page_view_test.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n<html>\n<head>\n    <script type=\"text/javascript\" src=\"/static/jquery/jquery-1.4.2.min.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/jquery/jquery-ui-1.8.10.custom.min.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/dirigible/tests/test_utils.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/json/json2.js\"></script>\n    <script src=\"yuitest/yuitest-combo.js\"></script>\n    <script src=\"jsmock.js\"></script>\n\n    <script src=\"/static/dirigible/scripts/page_view.js\"></script>\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"logger.css\" />\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"testlogger.css\" />\n</head>\n\n<body>\n<div id=\"id_results\">Please wait</div>\n\n<div id=\"id_sheet_name\"></div>\n\n\n<script type=\"text/javascript\">\n\ntests = [\n\n    new YAHOO.tool.TestCase({\n\n        name: \"testPageView\",\n\n        setUp: function() {\n            this.mockControl = new MockControl();\n            this.mockConsoleView = this.mockControl.createMock({\n                updateMetaData: function() {}\n            });\n            this.mockUsercodeView = this.mockControl.createMock({\n                updateMetaData: function() {}\n            });\n            this.mockGridView = this.mockControl.createMock({\n                updateMetaData: function() {}\n            });\n            this.pageView = new Dirigible.PageView(\n                'test username',\n                this.mockConsoleView,\n                this.mockUsercodeView,\n                this.mockGridView\n            );\n        },\n\n        testUpdateMetaDataWithNoServerDataShouldDoNothing: function() {\n            this.pageView.updateMetaData(null);\n            this.mockControl.verify();\n        },\n\n        testUpdateMetaDataWithUndefinedServerDataShouldDoNothing: function() {\n            this.pageView.updateMetaData();\n            this.mockControl.verify();\n        },\n\n        testUpdateMetaDataWithServerDataShouldDelegateAndSetSheetNameAndCallUpdatePageTitle: function() {\n            $('#id_sheet_name').text(\"Old name\");\n            var mockData = {\n                hello: 1,\n                name: \"New name\"\n            };\n\n            var titleUpdater = this.mockControl.createMock({\n                updatePageTitle: function() {}\n            });\n            this.pageView.updatePageTitle = titleUpdater.updatePageTitle;\n\n            this.mockConsoleView.expects().updateMetaData(mockData);\n            this.mockUsercodeView.expects().updateMetaData(mockData);\n            this.mockGridView.expects().updateMetaData(mockData);\n            titleUpdater.expects().updatePageTitle();\n\n            this.pageView.updateMetaData(mockData);\n\n            this.mockControl.verify();\n            YAHOO.util.Assert.areSame(\n                \"New name\",\n                $('#id_sheet_name').text()\n            );\n        },\n\n        testUpdatePageTitleShouldUpdateTitleOfPage: function() {\n            $('#id_sheet_name').text(\"test sheet name\");\n            this.pageView.updatePageTitle();\n            YAHOO.util.Assert.areSame(\"test username's test sheet name: Dirigible\", document.title);\n        }\n\n    })\n\n];\n</script>\n\n\n<script src=\"yuirunner.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/tests/security_settings_test.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n<html>\n<head>\n    <script type=\"text/javascript\" src=\"/static/jquery/jquery-1.4.2.min.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/jquery/jquery-ui-1.8.10.custom.min.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/dirigible/tests/test_utils.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/jquery/jquery.caret.1.02.min.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/json/json2.js\"></script>\n    <script src=\"yuitest/yuitest-combo.js\"></script>\n    <script src=\"jsmock.js\"></script>\n\n    <script src=\"/static/dirigible/scripts/security_settings.js\"></script>\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"logger.css\" />\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"testlogger.css\" />\n</head>\n\n<body>\n<div id=\"id_results\">Please wait</div>\n\n<input id=\"id_test\" />\n\n<script type=\"text/javascript\">\n\ntests = [\n\n\n    new YAHOO.tool.TestCase({\n\n        name: \"testSecuritySettingsModel\",\n\n        setUp: function() {\n            this.mockControl = new MockControl();\n\n            this.mockJQuery = this.mockControl.createMock({\n                find: function() {}\n            });\n\n            this.mockJQueryPoster  = this.mockControl.createMock({\n                ajax: function() {}\n            });\n            this.mockJQuery.find.ajax = this.mockJQueryPoster.ajax;\n\n            this.urls = {\n                setSecuritySettings: \"set security settings URL\",\n            }\n\n            this.model = new Dirigible.SecuritySettings.Model(this.mockJQuery.find, this.urls);\n        },\n\n\n        testUpdatePageUIShouldUpdateToolbarButtonTitleAndAltForEnabledAndPrivate: function() {\n            var mockSecurityButton = this.mockControl.createMock({\n                attr: function() {}\n            });\n            this.mockJQuery.expects().find('#id_security_button').andReturn(mockSecurityButton);\n\n            mockSecurityButton.expects().attr('alt', 'Sheet security settings (Sheet is private. JSON API enabled)');\n            mockSecurityButton.expects().attr('title', 'Sheet security settings (Sheet is private. JSON API enabled)');\n\n            this.model.enabled = true;\n            this.model.isPublic = false;\n            this.model.updatePageUI();\n\n            this.mockControl.verify();\n        },\n\n\n        testUpdatePageUIShouldUpdateToolbarButtonTitleAndAltForDisabledAndPrivate: function() {\n            var mockSecurityButton = this.mockControl.createMock({\n                attr: function() {}\n            });\n            this.mockJQuery.expects().find('#id_security_button').andReturn(mockSecurityButton);\n\n            mockSecurityButton.expects().attr('alt', 'Sheet security settings (Sheet is private. JSON API disabled)');\n            mockSecurityButton.expects().attr('title', 'Sheet security settings (Sheet is private. JSON API disabled)');\n\n            this.model.enabled = false;\n            this.model.isPublic = false;\n            this.model.updatePageUI();\n\n            this.mockControl.verify();\n        },\n\n\n        testUpdatePageUIShouldUpdateToolbarButtonTitleAndAltForEnabledAndPublic: function() {\n            var mockSecurityButton = this.mockControl.createMock({\n                attr: function() {}\n            });\n            this.mockJQuery.expects().find('#id_security_button').andReturn(mockSecurityButton);\n\n            mockSecurityButton.expects().attr('alt', 'Sheet security settings (Sheet is public. JSON API enabled)');\n            mockSecurityButton.expects().attr('title', 'Sheet security settings (Sheet is public. JSON API enabled)');\n\n            this.model.enabled = true;\n            this.model.isPublic = true;\n            this.model.updatePageUI();\n\n            this.mockControl.verify();\n        },\n\n\n        testUpdatePageUIShouldUpdateToolbarButtonTitleAndAltForDisabledAndPublic: function() {\n            var mockSecurityButton = this.mockControl.createMock({\n                attr: function() {}\n            });\n            this.mockJQuery.expects().find('#id_security_button').andReturn(mockSecurityButton);\n\n            mockSecurityButton.expects().attr('alt', 'Sheet security settings (Sheet is public. JSON API disabled)');\n            mockSecurityButton.expects().attr('title', 'Sheet security settings (Sheet is public. JSON API disabled)');\n\n            this.model.enabled = false;\n            this.model.isPublic = true;\n            this.model.updatePageUI();\n\n            this.mockControl.verify();\n        },\n\n\n        testUpdateServerStateShouldSendGivenStateToServerWithDataAndCallbacksCurriedIntoPostSuccessHandlerAndErrorCallbackAsErrorHandler: function() {\n            var ajaxSettings;\n            this.mockJQueryPoster.expects().ajax(\n                TypeOf.isA(Object)\n            ).andStub(\n                function () {\n                    ajaxSettings = arguments[0];\n                }\n            )\n\n            var mockHandleServerUpdateResponse = this.mockControl.createMock({\n                handleServerUpdateResponse: function() {}\n            });\n            this.model.handleServerUpdateResponse = mockHandleServerUpdateResponse.handleServerUpdateResponse;\n\n            this.model.updateServerState(\n                'public sheet state',\n                'enabled checkbox state',\n                'api key',\n                'mock success handler function',\n                'mock error handler function'\n            );\n\n            this.mockControl.verify();\n            var postSuccessHandler = ajaxSettings.success;\n            delete ajaxSettings.success;\n            assertDeepAreSame(\n                {\n                    type: \"POST\",\n                    url: this.urls.setSecuritySettings,\n                    data: {\n                        'is_public': 'public sheet state',\n                        'allow_json_api_access': 'enabled checkbox state',\n                        'api_key': 'api key'},\n                    error: 'mock error handler function'\n                },\n                ajaxSettings\n            );\n\n            this.mockControl.reset();\n            mockHandleServerUpdateResponse.expects().handleServerUpdateResponse(\n                'mock response data',\n                'public sheet state',\n                'enabled checkbox state',\n                'api key',\n                'mock success handler function',\n                'mock error handler function'\n            );\n            postSuccessHandler('mock response data');\n            this.mockControl.verify();\n        },\n\n\n        testHandleServerUpdateResponseShouldCallSuccessHandlerSaveSettingsFromParamsAndUpdatePageUIIfSuccessful: function() {\n            var mockSuccessHandler = this.mockControl.createMock({\n                onSuccess: function() {}\n            });\n            mockSuccessHandler.expects().onSuccess();\n\n            var mockFailureHandler = this.mockControl.createMock({\n                onFailure: function() {}\n            });\n\n            var mockPageUIUpdater = this.mockControl.createMock({\n                updatePageUI: function() {}\n            });\n            this.model.updatePageUI = mockPageUIUpdater.updatePageUI;\n            var that = this;\n            mockPageUIUpdater.expects().updatePageUI().andStub(\n                function() {\n                    YAHOO.util.Assert.areSame('public sheet state', that.model.isPublic);\n                    YAHOO.util.Assert.areSame('enabled checkbox state', that.model.enabled);\n                    YAHOO.util.Assert.areSame('api key', that.model.apiKey);\n                }\n            );\n\n            this.model.handleServerUpdateResponse(\n                'OK',\n                'public sheet state',\n                'enabled checkbox state',\n                'api key',\n                mockSuccessHandler.onSuccess,\n                mockFailureHandler.onFailure\n            );\n\n            this.mockControl.verify();\n        },\n\n\n        testHandleServerUpdateResponseShouldCallFailureHandlerIfUnsuccessful: function() {\n            var mockSuccessHandler = this.mockControl.createMock({\n                onSuccess: function() {}\n            });\n\n            var mockFailureHandler = this.mockControl.createMock({\n                onFailure: function() {}\n            });\n            mockFailureHandler.expects().onFailure();\n\n            this.model.handleServerUpdateResponse(\n                'FAIL',\n                'public sheet checkbox state',\n                'enabled checkbox state',\n                'api key',\n                mockSuccessHandler.onSuccess,\n                mockFailureHandler.onFailure\n            );\n\n            this.mockControl.verify();\n        }\n\n    }),\n\n\n    new YAHOO.tool.TestCase({\n\n        name: \"testSecuritySettingsDialog\",\n\n        setUp: function() {\n            this.mockControl = new MockControl();\n\n            this.mockJQuery = this.mockControl.createMock({\n                find: function() {}\n            });\n\n            //this.mockJQueryPoster  = this.mockControl.createMock({\n            //    : function() {}\n            //});\n            //this.mockJQuery.find.ajax = this.mockJQueryPoster.ajax;\n\n            this.urls = {\n                apiBase: \"API base\"\n            }\n            this.dialog = new Dirigible.SecuritySettings.Dialog(this.mockJQuery.find, this.urls);\n        },\n\n\n        testShowShouldStashAwayModelAndUpdateDialogUIAndShow: function() {\n            var mockModel = {\n                isPublic: \"model public state\",\n                enabled: \"model enabled state\",\n                apiKey: \"model api key\"\n            };\n\n            var mockErrorDiv = this.mockControl.createMock({\n                addClass: function() {}\n            });\n            this.mockJQuery.expects().find('#id_security_form_save_error').andReturn(mockErrorDiv);\n            mockErrorDiv.expects().addClass('hidden');\n\n            var mockDialogUpdater = this.mockControl.createMock({\n                updateDialogUI: function() {}\n            });\n            this.dialog.updateDialogUI = mockDialogUpdater.updateDialogUI;\n            mockDialogUpdater.expects().updateDialogUI(mockModel.isPublic, mockModel.enabled, mockModel.apiKey);\n\n            var mockDialogDiv = this.mockControl.createMock({\n                dialog: function() {}\n            });\n            this.mockJQuery.expects().find('#id_security_form').andReturn(mockDialogDiv);\n            var dialogOptions;\n            mockDialogDiv.expects().dialog(\n                TypeOf.isA(Object)\n            ).andStub(\n                function() {\n                    dialogOptions = arguments[0]\n                }\n            );\n\n            this.dialog.show(mockModel);\n\n            this.mockControl.verify();\n            YAHOO.util.Assert.areSame(mockModel, this.dialog.model__);\n            assertDeepAreSame(\n                { width: 400 },\n                dialogOptions\n            );\n        },\n\n\n        testUpdateDialogUIShouldEnableJSONAPIFieldsIfAPIEnabledAndSheetPublicAndUpdateCheckboxes: function() {\n            var mockJSONCheckbox = this.mockControl.createMock({\n                attr: function() {}\n            });\n            this.mockJQuery.expects().find('#id_security_form_json_enabled_checkbox').andReturn(mockJSONCheckbox);\n            mockJSONCheckbox.expects().attr('checked', true);\n\n            var mockPublicCheckbox = this.mockControl.createMock({\n                attr: function() {}\n            });\n            this.mockJQuery.expects().find('#id_security_form_public_sheet_checkbox').andReturn(mockPublicCheckbox);\n            mockPublicCheckbox.expects().attr('checked', true);\n\n            var mockAPIKeyField = this.mockControl.createMock({\n                attr: function() {},\n                val: function() {}\n            });\n            var mockAPIURLField = this.mockControl.createMock({\n                attr: function() {}\n            });\n            this.mockJQuery.expects().find('#id_security_form_json_api_key').andReturn(mockAPIKeyField);\n            this.mockJQuery.expects().find('#id_security_form_json_api_url').andReturn(mockAPIURLField);\n\n            mockAPIKeyField.expects().attr('disabled', '');\n            mockAPIURLField.expects().attr('disabled', '');\n            mockAPIKeyField.expects().val('newapikey');\n\n            var mockUpdateApiURL = this.mockControl.createMock({\n                updateAPIURL: function() {}\n            });\n            this.dialog.updateAPIURL = mockUpdateApiURL.updateAPIURL;\n            mockUpdateApiURL.expects().updateAPIURL();\n\n            this.dialog.updateDialogUI(true, true, 'newapikey');\n\n            this.mockControl.verify();\n        },\n\n\n        testUpdateDialogUIShouldDisableJSONAPIFieldsIfAPIDisabledAndSheetNotPublic: function() {\n            var mockJSONCheckbox = this.mockControl.createMock({\n                attr: function() {}\n            });\n            this.mockJQuery.expects().find('#id_security_form_json_enabled_checkbox').andReturn(mockJSONCheckbox);\n            mockJSONCheckbox.expects().attr('checked', false);\n\n            var mockPublicCheckbox = this.mockControl.createMock({\n                attr: function() {}\n            });\n            this.mockJQuery.expects().find('#id_security_form_public_sheet_checkbox').andReturn(mockPublicCheckbox);\n            mockPublicCheckbox.expects().attr('checked', false);\n\n            var mockAPIKeyField = this.mockControl.createMock({\n                attr: function() {},\n                val: function() {}\n            });\n            var mockAPIURLField = this.mockControl.createMock({\n                attr: function() {}\n            });\n            this.mockJQuery.expects().find('#id_security_form_json_api_key').andReturn(mockAPIKeyField);\n            this.mockJQuery.expects().find('#id_security_form_json_api_url').andReturn(mockAPIURLField);\n\n            mockAPIKeyField.expects().attr('disabled', 'disabled');\n            mockAPIURLField.expects().attr('disabled', 'disabled');\n            mockAPIKeyField.expects().val('newapikey');\n\n            var mockUpdateApiURL = this.mockControl.createMock({\n                updateAPIURL: function() {}\n            });\n            this.dialog.updateAPIURL = mockUpdateApiURL.updateAPIURL;\n            mockUpdateApiURL.expects().updateAPIURL();\n\n            this.dialog.updateDialogUI(false, false, 'newapikey');\n\n            this.mockControl.verify();\n        },\n\n\n        testOnOKShouldUpdateServerStateOnModelWithDialogValuesCloseFunctionAsSuccessHandlerAndErrorFunctionAsFailureHandler: function() {\n            var mockPublicCheckbox = this.mockControl.createMock({\n                attr: function() {}\n            });\n            this.mockJQuery.expects().find('#id_security_form_public_sheet_checkbox').andReturn(mockPublicCheckbox);\n            mockPublicCheckbox.expects().attr('checked').andReturn('public checkbox state');\n\n            var mockJSONCheckbox = this.mockControl.createMock({\n                attr: function() {}\n            });\n            this.mockJQuery.expects().find('#id_security_form_json_enabled_checkbox').andReturn(mockJSONCheckbox);\n            mockJSONCheckbox.expects().attr('checked').andReturn('enabled checkbox state');\n\n            var mockAPIKeyField = this.mockControl.createMock({\n                val: function() {}\n            });\n            this.mockJQuery.expects().find('#id_security_form_json_api_key').andReturn(mockAPIKeyField);\n            mockAPIKeyField.expects().val().andReturn('api key');\n\n            this.dialog.model__ = this.mockControl.createMock({\n                updateServerState: function() {}\n            });\n            this.dialog.model__.expects().updateServerState(\n                'public checkbox state',\n                'enabled checkbox state',\n                'api key',\n                this.dialog.close,\n                this.dialog.handleSaveError\n            );\n\n            var onResult = this.dialog.onOK();\n\n            this.mockControl.verify();\n\n            YAHOO.util.Assert.isFalse(onResult,\n                'onOK should return false to abort default form submission'\n            );\n        },\n\n\n        testCloseShouldCloseDialog: function() {\n            var mockForm = this.mockControl.createMock({\n                dialog: function() {}\n            });\n            this.mockJQuery.expects().find('#id_security_form').andReturn(mockForm);\n            mockForm.expects().dialog('close');\n\n            this.dialog.close();\n\n            this.mockControl.verify();\n        },\n\n\n        testHandleSaveErrorShouldShowErrorMessage: function() {\n            var mockErrorDiv = this.mockControl.createMock({\n                removeClass: function() {}\n            });\n            this.mockJQuery.expects().find('#id_security_form_save_error').andReturn(mockErrorDiv);\n            mockErrorDiv.expects().removeClass('hidden');\n\n            this.dialog.handleSaveError();\n\n            this.mockControl.verify();\n        },\n\n\n        testUpdateAPIUrlShouldBuildURLFromAPIKey: function() {\n            var mockAPIKeyField = this.mockControl.createMock({\n                val: function() {}\n            });\n            this.mockJQuery.expects().find('#id_security_form_json_api_key').andReturn(mockAPIKeyField);\n            mockAPIKeyField.expects().val().andReturn('api key');\n\n            var mockAPIURLLink  = this.mockControl.createMock({\n                attr: function() {},\n                val: function() {}\n            });\n            this.mockJQuery.expects().find('#id_security_form_json_api_url').andReturn(mockAPIURLLink);\n            server = window.location.host;\n            mockAPIURLLink.expects().val('http://' + server + 'API base?api_key=api key');\n\n            this.dialog.updateAPIURL();\n\n            this.mockControl.verify();\n        },\n\n        testBindShouldAttachEventsForFormElements: function() {\n            var mockOKButton = this.mockControl.createMock({\n                click: function() {}\n            });\n            this.mockJQuery.expects().find('#id_security_form_ok_button').andReturn(mockOKButton);\n            mockOKButton.expects().click(this.dialog.onOK);\n\n            var mockCancelbutton = this.mockControl.createMock({\n                click: function() {}\n            });\n            this.mockJQuery.expects().find('#id_security_form_cancel_button').andReturn(mockCancelbutton);\n            mockCancelbutton.expects().click(this.dialog.close);\n\n            var mockAPIKeyInput = this.mockControl.createMock({\n                bind: function() {}\n            });\n            this.mockJQuery.expects().find('#id_security_form_json_api_key').andReturn(mockAPIKeyInput);\n            mockAPIKeyInput.expects().bind(\n                'keyup mouseup change select',\n                this.dialog.updateAPIURL\n            );\n\n            var mockCheckbox = this.mockControl.createMock({\n                click: function() {}\n            });\n            this.mockJQuery.expects().find('#id_security_form_json_enabled_checkbox').andReturn(mockCheckbox);\n            mockCheckbox.expects().click(this.dialog.updateDialogUIFromCheckboxes);\n\n\n            var mockAPIURLInput = this.mockControl.createMock({\n                click: function() {}\n            });\n            var mockAPIKeyInput = this.mockControl.createMock({\n                click: function() {}\n            });\n            this.mockJQuery.expects().find('#id_security_form_json_api_url').andReturn(mockAPIURLInput);\n            mockAPIURLInput.expects().click(this.dialog.selectFullURL_);\n            this.mockJQuery.expects().find('#id_security_form_json_api_key').andReturn(mockAPIKeyInput);\n            mockAPIKeyInput.expects().click(this.dialog.selectFullURL_);\n\n            this.dialog.bind();\n\n            this.mockControl.verify();\n        },\n\n        testUpdateDialogUIFromCheckboxesShouldPassCheckboxStates: function() {\n            var mockJSONCheckbox = this.mockControl.createMock({\n                attr: function() {}\n            });\n            this.mockJQuery.expects().find('#id_security_form_json_enabled_checkbox').andReturn(mockJSONCheckbox);\n            mockJSONCheckbox.expects().attr('checked').andReturn('api checkboxstate');\n            var mockPublicCheckbox = this.mockControl.createMock({\n                attr: function() {}\n            });\n            this.mockJQuery.expects().find('#id_security_form_public_sheet_checkbox').andReturn(mockPublicCheckbox);\n            mockPublicCheckbox.expects().attr('checked').andReturn('public checkboxstate');\n\n            mockDialog = this.mockControl.createMock({\n                enableJsonApiFields_: function() {}\n            });\n            this.dialog.enableJsonApiFields_ = mockDialog.enableJsonApiFields_;\n            mockDialog.expects().enableJsonApiFields_('api checkboxstate');\n\n            this.dialog.updateDialogUIFromCheckboxes();\n\n            this.mockControl.verify();\n        },\n\n        testSelectFullURL: function() {\n            var dialog = new Dirigible.SecuritySettings.Dialog($, this.urls);\n            $test_input = $('#id_test');\n            $test_input.caret(2, 3);\n            $test_input.val('contents');\n            $test_input.click(dialog.selectFullURL_);\n\n            $test_input.trigger('click');\n\n            var caretAfter = $test_input.caret();\n            YAHOO.util.Assert.areSame( 0, caretAfter.start);\n            YAHOO.util.Assert.areSame('contents'.length, caretAfter.end);\n        }\n\n    })\n];\n</script>\n\n\n<script src=\"yuirunner.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/tests/selection_model_test.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n<html>\n<head>\n    <script type=\"text/javascript\" src=\"/static/json/json2.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/jquery/jquery-1.4.2.min.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/jquery/jquery-ui-1.8.10.custom.min.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/jquery/jquery.caret.1.02.min.js\"></script>\n\n    <script type=\"text/javascript\" src=\"yuitest/yuitest-combo.js\"></script>\n    <script type=\"text/javascript\" src=\"jsmock.js\"></script>\n    <script type=\"text/javascript\" src=\"test_utils.js\"></script>\n\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"logger.css\" />\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"testlogger.css\" />\n\n    <script type=\"text/javascript\" src=\"/static/jquery/jquery.event.drag-2.0.min.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/slickgrid/slick.core.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/slickgrid/slick.grid.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/slickgrid/slick.cellrangedecorator.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/slickgrid/slick.cellrangeselector.js\"></script>\n\n    <script type=\"text/javascript\" src=\"/static/dirigible/scripts/keyboard_cellrange_selector.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/dirigible/scripts/selection_model.js\"></script>\n</head>\n\n<body>\n<div id=\"id_results\">Please wait</div>\n\n<script type=\"text/javascript\">\n\n    tests = [\n\n        new YAHOO.tool.TestCase({\n\n            name: \"testSelectionModel\",\n\n            setUp: function() {\n                this.mockControl = new MockControl();\n                this.mockGrid = this.mockControl.createMock({\n                    registerPlugin: function() {},\n                    unregisterPlugin: function() {},\n                    setActiveCell: function() {},\n                    getActiveCell: function() {},\n                    getEditController: function() {},\n                    canCellBeSelected: function() {}\n                });\n                this.eventHandlerPattern = {\n                    subscribe : function() {},\n                    unsubscribe : function() {},\n                    notify :  function() {}\n                };\n                this.mockGrid.onActiveCellChanged = this.mockControl.createMock(this.eventHandlerPattern);\n            },\n\n\n            testConstructorShouldCreateSelector: function () {\n                var selectionModel = new Dirigible.SelectionModel();\n                YAHOO.util.Assert.areSame(2, selectionModel.selectors.length);\n                YAHOO.util.Assert.isTrue(\n                            selectionModel.selectors[0] instanceof Slick.CellRangeSelector\n                );\n                YAHOO.util.Assert.isTrue(\n                            selectionModel.selectors[1] instanceof Dirigible.KeyboardCellRangeSelector\n                );\n            },\n\n\n            testConstructorShouldCreateSelectedRangesChangedEvent: function () {\n                var selectionModel = new Dirigible.SelectionModel();\n                YAHOO.util.Assert.isTrue(selectionModel.onSelectedRangesChanged instanceof Slick.Event);\n            },\n\n\n            testConstructorShouldCreateEmptyRangesAttribute: function () {\n                var selectionModel = new Dirigible.SelectionModel();\n                assertDeepAreSame([], selectionModel.ranges);\n            },\n\n\n            testInitMethodShouldSubscribeToGridEventsRegisterPluginAndSubscribeToSelectorEvents: function () {\n                var selectionModel = new Dirigible.SelectionModel();\n                for (var i=0; i<2; i++) {\n                    selectionModel.selectors[i] = this.mockControl.createMock({});\n                    selectionModel.selectors[i].onBeforeCellRangeSelected = this.mockControl.createMock({subscribe: function() {}});\n                    selectionModel.selectors[i].onCellRangeSelected = this.mockControl.createMock({subscribe: function() {}});\n                }\n                selectionModel.getHandleActiveCellChanged = function() {return 'ActiveCellChanged handler';};\n                selectionModel.getHandleBeforeCellRangeSelected = function() {return 'HandleBeforeCellRangeSelected handler';};\n                selectionModel.getHandleCellRangeSelected = function() {return 'HandleCellRangeSelected handler';};\n\n                this.mockControl.reset();\n                this.mockGrid.onActiveCellChanged.expects().subscribe(\n                    'ActiveCellChanged handler'\n                );\n                for (var i=0; i<2; i++) {\n                    this.mockGrid.expects().registerPlugin(selectionModel.selectors[i]);\n                    selectionModel.selectors[i].onBeforeCellRangeSelected.expects().subscribe(\n                    'HandleBeforeCellRangeSelected handler'\n                    );\n                    selectionModel.selectors[i].onCellRangeSelected.expects().subscribe(\n                    'HandleCellRangeSelected handler'\n                    );\n                }\n                selectionModel.init(this.mockGrid);\n\n                this.mockControl.verify();\n\n                YAHOO.util.Assert.areSame(selectionModel.handleActiveCellChanged, 'ActiveCellChanged handler');\n                YAHOO.util.Assert.areSame(selectionModel.handleBeforeCellRangeSelected, 'HandleBeforeCellRangeSelected handler');\n                YAHOO.util.Assert.areSame(selectionModel.handleCellRangeSelected, 'HandleCellRangeSelected handler');\n            },\n\n\n            testDestroyMethodShouldUnsubscribeFromGridEventsUnregisterPluginAndUnsubscribeFromSelectorEvents: function () {\n                var selectionModel = new Dirigible.SelectionModel();\n                selectionModel.init(this.mockGrid);\n                this.mockControl.reset();\n\n                this.mockGrid.onActiveCellChanged = this.mockControl.createMock(this.eventHandlerPattern);\n                this.mockGrid.onActiveCellChanged.expects().unsubscribe(\n                    selectionModel.handleActiveCellChanged\n                );\n\n                for (var i=0; i<selectionModel.selectors.length; i++) {\n                    selectionModel.selectors[i].onBeforeCellRangeSelected = this.mockControl.createMock(this.eventHandlerPattern);\n                    selectionModel.selectors[i].onBeforeCellRangeSelected.expects().unsubscribe(\n                        selectionModel.handleBeforeCellRangeSelected\n                    );\n\n                    selectionModel.selectors[i].onCellRangeSelected = this.mockControl.createMock(this.eventHandlerPattern);\n                    selectionModel.selectors[i].onCellRangeSelected.expects().unsubscribe(\n                        selectionModel.handleCellRangeSelected\n                    );\n\n                    this.mockGrid.expects().unregisterPlugin(selectionModel.selectors[i]);\n                }\n\n                selectionModel.destroy();\n\n                this.mockControl.verify();\n            },\n\n\n            testGetSelectedRangesShouldReturnInternalAttribute : function () {\n                var selectionModel = new Dirigible.SelectionModel();\n                selectionModel.ranges = \"Something completely different\";\n                YAHOO.util.Assert.areSame(selectionModel.ranges, selectionModel.getSelectedRanges());\n            },\n\n\n            testSetSelectedRangesShouldSetInternalAttributeAndFireEvent : function () {\n                var selectionModel = new Dirigible.SelectionModel();\n                var newRanges = [\n                    {\n                        fromRow : 1, toRow : 5,\n                        fromCell : 12, toCell : 24\n                    },\n                    {\n                        fromRow : 2, toRow : 6,\n                        fromCell : 11, toCell : 22\n                    }\n                ];\n\n                selectionModel.onSelectedRangesChanged = this.mockControl.createMock(this.eventHandlerPattern);\n                selectionModel.onSelectedRangesChanged.expects().notify(newRanges);\n\n                selectionModel.setSelectedRanges(newRanges);\n                assertDeepAreSame(newRanges, selectionModel.ranges);\n                this.mockControl.verify();\n            },\n\n\n            testHandleCellRangeSelectedShouldCallSetSelectedRangesWithListContainingNewRange : function () {\n                var selectionModel = new Dirigible.SelectionModel();\n                selectionModel.init(this.mockGrid);\n                this.mockControl.reset();\n\n                var newRange = {\n                    fromRow : 1, toRow : 5,\n                    fromCell : 12, toCell : 24\n                };\n\n                var mockSetSelectedRangesParams = undefined;\n                selectionModel.setSelectedRanges = function (newSelectedRanges) {\n                    mockSetSelectedRangesParams = newSelectedRanges;\n                };\n\n                selectionModel.handleCellRangeSelected({}, { range: newRange });\n\n                assertDeepAreSame([newRange], mockSetSelectedRangesParams);\n            },\n\n            testHandleBeforeCellRangeSelectedShouldSetActiveCellOnGridAndCommitsCurrentEditIfCellIsSelectable: function () {\n                var selectionModel = new Dirigible.SelectionModel();\n                selectionModel.init(this.mockGrid);\n                this.mockControl.reset();\n\n                var mockController = this.mockControl.createMock({\n                    commitCurrentEdit: function() {}\n                });\n                this.mockGrid.expects().canCellBeSelected(4, 8).andReturn(true);\n                this.mockGrid.expects().getEditController().andReturn(mockController);\n                mockController.expects().commitCurrentEdit();\n                this.mockGrid.expects().setActiveCell(4, 8);\n\n                result = selectionModel.handleBeforeCellRangeSelected(\n                    {}, {row: 4, cell: 8}\n                );\n\n                YAHOO.util.Assert.isTrue(result);\n                this.mockControl.verify();\n            },\n\n            testHandleBeforeCellRangeSelectedShouldNotSetActiveCellOnGridOrCommitCurrentEditIfCellIsNotSelectable: function () {\n                var selectionModel = new Dirigible.SelectionModel();\n                selectionModel.init(this.mockGrid);\n                this.mockControl.reset();\n\n                var mockController = this.mockControl.createMock({\n                    commitCurrentEdit: function() {}\n                });\n                this.mockGrid.expects().canCellBeSelected(4, 8).andReturn(false);\n\n                result = selectionModel.handleBeforeCellRangeSelected(\n                    {}, {row: 4, cell: 8}\n                );\n\n                YAHOO.util.Assert.isTrue(result);\n                this.mockControl.verify();\n            },\n\n            testHandleActiveCellChangedClearsCurrentSelection: function () {\n                var selectionModel = new Dirigible.SelectionModel();\n                selectionModel.init(this.mockGrid);\n                selectionModel.ranges = ['some selected ranges before'];\n                this.mockControl.reset();\n                var cell = {row: 1, cell: 1};\n                this.mockGrid.expects().getActiveCell().andReturn(cell);\n\n                result = selectionModel.handleActiveCellChanged(null, {});\n\n                var expected_range = new Slick.Range(\n                    cell.row, cell.cell,\n                    cell.row, cell.cell);\n                assertDeepAreSame([expected_range], selectionModel.ranges);\n\n            }\n        }),\n\n        new YAHOO.tool.TestCase({\n\n            name: \"testKeyboardSelector\",\n\n            setUp: function() {\n                this.mockControl = new MockControl();\n                this.mockSelectionModel = this.mockControl.createMock({});\n                this.mockGrid = this.mockControl.createMock({\n                    registerPlugin: function() {},\n                    unregisterPlugin: function() {},\n                    setActiveCell: function() {},\n                    getActiveCell: function() {}\n                });\n                this.eventHandlerPattern = {\n                    subscribe : function() {},\n                    unsubscribe : function() {},\n                    notify :  function() {}\n                };\n                this.mockGrid.onActiveCellChanged = this.mockControl.createMock(this.eventHandlerPattern);\n                this.mockGrid.onKeyDown = this.mockControl.createMock(this.eventHandlerPattern);\n            },\n\n            testConstructor: function () {\n                var selector = new Dirigible.KeyboardCellRangeSelector();\n                YAHOO.util.Assert.isFalse(selector.isShiftDown);\n                YAHOO.util.Assert.areSame(null, selector.startCell);\n            },\n\n\n            testInitStoresGridAndSubscribesToGridKeyDownAndOnActiveCellChangedAndWindowKeyUp: function () {\n                var handler_called = false;\n                var selector = new Dirigible.KeyboardCellRangeSelector();\n                selector.getKeyDownHandler = function () { return 'handlie'; };\n                selector.getKeyUpHandler = function () { return function () {handler_called = true;}; };\n                selector.getActiveCellChangedHandler = function () { return 'handlie 2'; };\n\n                this.mockGrid.onKeyDown.expects().subscribe('handlie');\n                this.mockGrid.onActiveCellChanged.expects().subscribe('handlie 2');\n                selector.init(this.mockGrid);\n\n                this.mockControl.verify();\n                $(window.document).trigger('keyup');\n                YAHOO.util.Assert.isTrue(handler_called);\n                YAHOO.util.Assert.areSame(selector.grid, this.mockGrid);\n\n\n                //YAHOO.util.Assert.isTrue(selector.decorator instanceof Slick.CellRangeDecorator);\n                //this.decorator = new Slick.CellRangeDecorator(grid);\n\n            },\n\n            testKeyDownHandlerSavesActiveCellandSetsKeyDownFlag: function() {\n                var selector = new Dirigible.KeyboardCellRangeSelector();\n                selector.init(this.mockGrid);\n                this.mockControl.reset();\n\n                YAHOO.util.Assert.isFalse(selector.isShiftDown);\n\n                var handler = selector.getKeyDownHandler();\n                var some_event = {which: 35};\n\n                handler(some_event);\n\n                YAHOO.util.Assert.isFalse(selector.isShiftDown);\n\n                var some_event = {which: 16};\n                this.mockGrid.expects().getActiveCell().andReturn('active cell');\n\n                handler(some_event);\n\n                this.mockControl.verify();\n                YAHOO.util.Assert.isTrue(selector.isShiftDown);\n                YAHOO.util.Assert.areSame('active cell', selector.startCell);\n            },\n\n            testKeyDownHandler_TabClearsSelectionAndNavigates: function ()\n            {\n                var TAB = 9;\n\n                var selector = new Dirigible.KeyboardCellRangeSelector();\n                selector.init(this.mockGrid);\n                selector.isShiftDown = true;\n                selector.startCell = {row: 1, cell: 2};\n                this.mockControl.reset();\n\n                var handler = selector.getKeyDownHandler();\n                var some_event = {which: TAB};\n\n                handler(some_event);\n\n                YAHOO.util.Assert.isFalse(selector.isShiftDown);\n                YAHOO.util.Assert.areSame(null, selector.startCell);\n            },\n\n            testKeyUpHandlerShouldClearStartCellAndShiftFlag: function() {\n                var selector = new Dirigible.KeyboardCellRangeSelector();\n\n                selector.isShiftDown = true;\n                var start_cell = {row: 1, cell: 2};\n                var some_other_cell = {row: 2, cell: 3};\n\n                selector.startCell = start_cell;\n                var handler = selector.getKeyUpHandler();\n\n                var not_shift = 35;\n                var shift = 16;\n\n                var some_event = {which: not_shift};\n                handler(some_event, some_other_cell);\n                YAHOO.util.Assert.isTrue(selector.isShiftDown);\n                YAHOO.util.Assert.areSame(start_cell, selector.startCell);\n\n                var some_event = {which: shift};\n                handler(some_event, some_other_cell);\n                YAHOO.util.Assert.isFalse(selector.isShiftDown);\n                YAHOO.util.Assert.areSame(null, selector.startCell);\n            },\n\n            testActiveCellChangedHandlerExtendsSelectionIfKeyDownByNotifyingEvent: function() {\n                var selector = new Dirigible.KeyboardCellRangeSelector();\n                var handler = selector.getActiveCellChangedHandler();\n\n                var actual_range;\n                selector.onCellRangeSelected.notify = function (range_object) {\n                    actual_range = range_object.range;\n                };\n\n                selector.isShiftDown = true;\n                selector.startCell = {row: 5, cell: 5};\n                var some_event = {};\n                var new_cell = {row: 4, cell: 6};\n\n                var expected_range = new Slick.Range(5, 5, 4, 6);\n\n                handler(some_event, new_cell);\n\n                assertDeepAreSame(expected_range, actual_range);\n            },\n\n            testActiveCellChangedHandlerDoesNothingIfstartCellNull: function() {\n                var selector = new Dirigible.KeyboardCellRangeSelector();\n                var handler = selector.getActiveCellChangedHandler();\n\n                selector.onCellRangeSelected.notify = function (range_object) {\n                    YAHOO.util.Assert.fail('should not have notified if start cell null');\n                };\n\n                selector.isShiftDown = true;\n                var some_event = {};\n                var new_cell = {row: 4, cell: 6};\n\n                selector.startCell = null;\n                handler(some_event, new_cell);\n            },\n\n            testActiveCellChangedHandlerDoesNothingIfShiftNotDown: function() {\n                var selector = new Dirigible.KeyboardCellRangeSelector();\n                var handler = selector.getActiveCellChangedHandler();\n\n                selector.onCellRangeSelected.notify = function (range_object) {\n                    YAHOO.util.Assert.fail('should not have notified if shiftdown false');\n                };\n\n                var some_event = {};\n                var new_cell = {row: 4, cell: 6};\n                selector.isShiftDown = false;\n                selector.startCell = new_cell;\n\n                handler(some_event, new_cell);\n\n            }\n        })\n    ];\n</script>\n<script src=\"yuirunner.js\"></script>\n\n<div id=\"id_grid\"></div>\n\n</body>\n\n</html>\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/tests/sheet_page_utils_test.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n<html>\n<head>\n    <script type=\"text/javascript\" src=\"/static/jquery/jquery-1.4.2.min.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/dirigible/tests/test_utils.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/json/json2.js\"></script>\n    <script src=\"yuitest/yuitest-combo.js\"></script>\n    <script src=\"jsmock.js\"></script>\n\n    <script src=\"../scripts/sheet_page_utils.js\"></script>\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"logger.css\" />\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"testlogger.css\" />\n</head>\n\n<body>\n<div id=\"id_results\">Please wait</div>\n\n\n<script type=\"text/javascript\">\n\n    tests = [\n\n        new YAHOO.tool.TestCase({\n\n            name: \"RecalculationQueueFunctionsTest\",\n\n            setUp : function () {\n                this.mockControl = new MockControl();\n                this.mockJQuery = this.mockControl.createMock({\n                    ajaxq: function() {}\n                });\n                $.extend(true, $, { ajaxq: this.mockJQuery.ajaxq });\n\n                this.initURLs = {\n                    calculate: 'calculateURL'\n                };\n                this.mockPageView = {\n                    updateMetaData: function () {}\n                };\n                Dirigible.SheetUtils.Initialise(this.initURLs, this.mockPageView);\n            },\n\n\n            testQueueRecalculation: function () {\n                var params;\n                this.mockJQuery.expects().ajaxq(\n                    'recalculation_queue',\n                    TypeOf.isA(Object)\n                ).andStub(\n                    function() { params = arguments[1]; }\n                );\n\n                Dirigible.SheetUtils.queueRecalculation();\n\n                this.mockControl.verify();\n\n                assertDeepAreSame(\n                    {\n                        type: 'get',\n                        url: this.initURLs.calculate,\n                        success: Dirigible.SheetUtils.getMetaData\n                    },\n                    params\n                );\n            },\n\n\n            testAbortOtherRecalculations: function () {\n                this.mockJQuery.expects().ajaxq('recalculation_queue');\n\n                Dirigible.SheetUtils.abortOtherRecalculations();\n\n                this.mockControl.verify();\n            }\n\n        }),\n\n\n        new YAHOO.tool.TestCase({\n\n            name: \"GetMetaDataTest\",\n\n            setUp : function () {\n                this.ajaxqParamsList = [];\n                var that = this;\n                $.extend(true, $, {\n                    'ajaxq': function (queueName, params) {\n                        that.ajaxqParamsList.push({ queueName: queueName, params: params });\n                    }\n                });\n\n                this.initURLs = {\n                    getJSONMetaData: 'getJSONMetaDataURL'\n                };\n                this.mockPageView = {\n                    updateMetaData: function () {}\n                };\n                Dirigible.SheetUtils.Initialise(this.initURLs, this.mockPageView);\n            },\n\n\n            testGetMetaDataDoesNothingIfContentNotOK: function () {\n                Dirigible.SheetUtils.getMetaData('NotOK');\n\n                assertDeepAreSame(undefined, this.ajaxqParamsList[0]);\n                assertDeepAreSame(undefined, this.ajaxqParamsList[1]);\n            },\n\n\n            testGetMetaDataUpdatesMetaDataIfContentOK: function () {\n                Dirigible.SheetUtils.getMetaData('OK');\n\n                var firstCallArgs = { queueName: 'get_json_queue' };\n                assertDeepAreSame(firstCallArgs, this.ajaxqParamsList[0]);\n\n                var secondCallArgs = {\n                    queueName: 'get_json_queue',\n                    params: {\n                        type: 'get',\n                        dataType: 'json',\n                        url: this.initURLs.getJSONMetaData,\n                        success: this.mockPageView.updateMetaData\n                    }\n                };\n                assertDeepAreSame(secondCallArgs, this.ajaxqParamsList[1]);\n            }\n\n        })\n\n\n    ];\n</script>\n\n\n<script src=\"yuirunner.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/tests/test_utils.js",
    "content": "// Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n// See LICENSE.md\n//\n\n\nfunction elementVisible(element, expected, message) {\n    if (message == undefined) {\n        message = '';\n    } else {\n        message = ': ' + message\n    }\n    YAHOO.util.Assert.isTrue(element.length > 0, 'element(s) should exist' + message);\n    actual = element.is(':visible');\n    var ids = '';\n    element.each(function () {ids += this.id;});\n    YAHOO.util.Assert.areSame(expected, actual, 'wrong visibility for ' + ids + message);\n}\n\n\nfunction assertDeepAreSame(expected, actual, message) {\n    if (message == undefined) {\n        message = \"<root object>\";\n    } else {\n        message = message + \"  Path: <root object>\";\n    }\n\n    var finalMessage = _deepAreSame(expected, actual, message);\n    if (finalMessage){\n        throw new YAHOO.util.ComparisonFailure(finalMessage, expected, actual);\n    }\n\n}\n\n\nfunction _deepAreSame(expected, actual, message) {\n    if (typeof(expected) !== typeof(actual)) {\n        if (actual === undefined) {\n            return message + \" property missing from actual\";\n        }\n        return (\n            message + ' types differ ' +\n            '(expected is ' + typeof(expected) +\n            ', actual is ' + typeof(actual) + ')'\n        );\n    }\n\n    switch (typeof(expected)) {\n        case 'object':\n            for (p in expected) {\n                var recurseDown = _deepAreSame(\n                        expected[p], actual[p], message + '.' + p\n                );\n                if (recurseDown) {\n                    return recurseDown;\n                }\n            }\n            break;\n\n        case 'function':\n            if (typeof(actual) == 'undefined'\n                ||\n                (expected.toString() != actual.toString())\n            ) {\n                return (\n                    message + \" methods exist on both \" +\n                    \"expected and actual but differ\\n\" +\n                    \"expected : \" + expected.toString() + \"\\n\" +\n                    \"actual : \" + actual.toString()\n                );\n            }\n            break;\n\n        default:\n            if (expected !== actual){\n                return (\n                    message + ' property differs, ' +\n                    expected + ' !== ' + actual\n                );\n            }\n            break;\n    }\n\n    for (p in actual) {\n        if (\n                typeof(actual[p]) != 'undefined' &&\n                typeof(expected[p]) == 'undefined'\n        ) {\n            return message + \".\" + p + \" property unexpectedly on actual\";\n        }\n    }\n}\n\n\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/tests/test_utils_test.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n<html>\n<head>\n    <script type=\"text/javascript\" src=\"/static/jquery/jquery-1.4.2.min.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/dirigible/tests/test_utils.js\"></script>\n    <script src=\"yuitest/yuitest-combo.js\"></script>\n    <script src=\"jsmock.js\"></script>\n\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"logger.css\" />\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"testlogger.css\" />\n</head>\n\n<body>\n<div id=\"id_results\">Please wait</div>\n\n\n<script type=\"text/javascript\">\n\n    tests = [\n\n        new YAHOO.tool.TestCase({\n\n            name: \"testDeepAreSame\",\n\n            testStrings: function () {\n                var message = 'msg';\n                YAHOO.util.Assert.areSame('msg property differs, aaa !== aBB', _deepAreSame('aaa', 'aBB', message));\n            },\n            testInteger: function () {\n                var message = 'msg';\n                YAHOO.util.Assert.areSame('msg property differs, 1 !== 2', _deepAreSame(1, 2, message));\n            },\n            testActualMissingProperty: function () {\n                var message = '_';\n                var actual = {foo: 'bar'};\n                var expected = {bar : 'foo'};\n                YAHOO.util.Assert.areSame('_.bar property missing from actual', _deepAreSame(expected, actual, message));\n            },\n            testActualSuperfluousProperty: function () {\n                var message = '_';\n                var actual = {bar: 'foo', foo: 'bar'};\n                var expected = {bar: 'foo'};\n                YAHOO.util.Assert.areSame('_.foo property unexpectedly on actual', _deepAreSame(expected, actual, message));\n            },\n            testExpectedFalsyProperty: function () {\n                var message = '_';\n                var actual = {bar: 'foo', foo: true};\n                var expected = {bar: 'foo', foo: false};\n                YAHOO.util.Assert.areSame('_.foo property differs, false !== true', _deepAreSame(expected, actual, message));\n            },\n\n\n            testPropertyValueDiffers: function () {\n                var message = '_';\n                var actual = {bar: 'bar'};\n                var expected = {bar: 'foo'};\n                YAHOO.util.Assert.areSame('_.bar property differs, foo !== bar', _deepAreSame(expected, actual, message));\n            },\n            testNestedPropertyValueDiffers: function () {\n                var message = '_';\n                var actual = {bar: {breakfast: 'eggs'}};\n                var expected = {bar: {breakfast: 'sausages'}};\n                YAHOO.util.Assert.areSame('_.bar.breakfast property differs, sausages !== eggs', _deepAreSame(expected, actual, message));\n            },\n            testArrays: function () {\n                var message = '_';\n                var actual = [1, 2, 3, 5];\n                var expected = [1, 2, 3, 666];\n                YAHOO.util.Assert.areSame('_.3 property differs, 666 !== 5', _deepAreSame(expected, actual, message));\n            },\n            testFunctions: function () {\n                var message = '_';\n                var actual = {mine: function () {return 'yay';}};\n                var expected = {mine: function () {return 'boo';}};\n                var expectedMessage = '_.mine methods exist on both expected and actual but differ\\n';\n                YAHOO.util.Assert.areSame(0, _deepAreSame(expected, actual, message).indexOf(expectedMessage));\n            },\n            testFalsyObjects: function () {\n                YAHOO.util.Assert.areSame('_ types differ (expected is string, actual is object)', _deepAreSame('', {}, '_'));\n            }\n\n        }),\n        new YAHOO.tool.TestCase({\n\n            name: \"testAssertDeepAreSame\",\n\n            testAssertRaisesOnAnyStringReturnFromDeepAreSame: function () {\n                assertDeepAreSame(1, 1, 'message'); // doesn't raise\n\n                var should_raise = true;\n                try {\n                    assertDeepAreSame('1', 2, 'message');\n                    should_raise = false;\n                }\n                catch(e) {}\n                YAHOO.util.Assert.isTrue(should_raise);\n            }\n    })\n\n    ];\n</script>\n\n<script src=\"yuirunner.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/tests/testlogger.css",
    "content": "/*\nCopyright (c) 2008, Yahoo! Inc. All rights reserved.\nCode licensed under the BSD License:\nhttp://developer.yahoo.net/yui/license.txt\nversion: 2.6.0\n*/\n.yui-log {padding-top:3em;}\n/*\n.yui-log-container {position:relative;width:60em}\n.yui-log .yui-log-bd {height:auto; overflow:visible;}\n*/\n.yui-log-container {width:70em}\n.yui-log .yui-log-bd {height:60em}\n.yui-log .yui-log-btns {display:none;}\n.yui-log .yui-log-ft .yui-log-sourcefilters {visibility:hidden;}\n.yui-log .yui-log-hd {display:none;}\n.yui-log .yui-log-ft {position:absolute;top:0em;}\n\n.pass {\n    background-color: green;\n    font-weight: bold;\n    color: white;\n}\n.fail {\n    background-color: red;\n    font-weight: bold;\n    color: white;\n}\n.ignore {\n    background-color: #666;\n    font-weight: bold;\n    color: white;\n}\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/tests/toolbar_interaction_handler_test.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n<html>\n<head>\n    <script type=\"text/javascript\" src=\"/static/jquery/jquery-1.4.2.min.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/dirigible/tests/test_utils.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/json/json2.js\"></script>\n    <script src=\"yuitest/yuitest-combo.js\"></script>\n    <script src=\"jsmock.js\"></script>\n\n    <script src=\"../scripts/toolbar_interaction_handler.js\"></script>\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"logger.css\" />\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"testlogger.css\" />\n</head>\n\n<body>\n<div id=\"id_results\">Please wait</div>\n\n<img id='id_cut_button' alt=\"Cut\" title=\"Cut\" />\n<img id='id_copy_button' alt=\"Copy\" title=\"Copy\" />\n<img id='id_paste_button' alt=\"Paste\" title=\"Paste\" />\n<img id='id_recalc_button' alt=\"Recalculate\" title=\"Recalculate\" />\n\n\n<script type=\"text/javascript\">\n\n    tests = [\n\n        new YAHOO.tool.TestCase({\n\n            name: \"testToolbarInteractionHandler\",\n\n            testBind_BindsEvents: function () {\n                var mockControl = new MockControl();\n                var gridCommands = mockControl.createMock({\n                    cut: function() {},\n                    copy: function() {},\n                    paste: function() {}\n                });\n                var pageCommands = mockControl.createMock({\n                    recalculate: function() {}\n                });\n\n                gridCommands.expects().cut(TypeOf.isA(Object));\n                gridCommands.expects().copy(TypeOf.isA(Object));\n                gridCommands.expects().paste(TypeOf.isA(Object));\n                pageCommands.expects().recalculate(TypeOf.isA(Object));\n\n                var handler = new Dirigible.ToolbarInteractionHandler(\n                    gridCommands, pageCommands\n                );\n                handler.bind();\n                $('#id_cut_button').trigger('click');\n                $('#id_copy_button').trigger('click');\n                $('#id_paste_button').trigger('click');\n                $('#id_recalc_button').trigger('click');\n\n                mockControl.verify();\n            }\n\n        })\n\n    ];\n</script>\n\n\n<script src=\"yuirunner.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/tests/usercode_view_test.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n<html>\n<head>\n    <script src=\"/static/ace/ace-uncompressed.js\" type=\"text/javascript\" charset=\"utf-8\"></script>\n    <script src=\"/static/ace/mode-python.js\" type=\"text/javascript\" charset=\"utf-8\"></script>\n    <script type=\"text/javascript\" src=\"/static/jquery/jquery-1.4.2.min.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/jquery/jquery-ui-1.8.10.custom.min.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/dirigible/tests/test_utils.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/json/json2.js\"></script>\n    <script src=\"yuitest/yuitest-combo.js\"></script>\n    <script src=\"jsmock.js\"></script>\n\n    <script src=\"/static/dirigible/scripts/usercode_view.js\"></script>\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"logger.css\" />\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"testlogger.css\" />\n</head>\n\n<body>\n<div id=\"id_results\">Please wait</div>\n\n\n<script type=\"text/javascript\">\n\n    tests = [\n\n        new YAHOO.tool.TestCase({\n\n            name: \"testUsercodeView\",\n\n            testInitialiseComponentsShouldSetPythonModeAndReadOnlyTrueIfEditableFalse: function() {\n                var mockControl = new MockControl();\n                var mockEditor = mockControl.createMock( {\n                    getSession: function() {},\n                    setReadOnly: function() {}\n                } );\n                var mockSession = mockControl.createMock( {\n                    setMode: function() {}\n                } );\n                var ucView = new Dirigible.UsercodeView(mockEditor);\n                mockEditor.expects().getSession().andReturn(mockSession);\n                var modeType = require('ace/mode/python').Mode;\n                mockSession.expects().setMode(TypeOf.isA(modeType));\n                mockEditor.expects().setReadOnly(true);\n\n                ucView.initialiseComponents(false);\n\n                mockControl.verify();\n            },\n\n            testInitialiseComponentsShouldSetPythonModeAndReadOnlyFalseIfEditableTrue: function() {\n                var mockControl = new MockControl();\n                var mockEditor = mockControl.createMock( {\n                    getSession: function() {},\n                    setReadOnly: function() {}\n                } );\n                var mockSession = mockControl.createMock( {\n                    setMode: function() {}\n                } );\n                var ucView = new Dirigible.UsercodeView(mockEditor);\n                mockEditor.expects().getSession().andReturn(mockSession);\n                var modeType = require('ace/mode/python').Mode;\n                mockSession.expects().setMode(TypeOf.isA(modeType));\n                mockEditor.expects().setReadOnly(false);\n\n                ucView.initialiseComponents(true);\n\n                mockControl.verify();\n            },\n\n            testUpdateMetaDataPutsErrorsIntoEditorAnnotations: function () {\n                var mockControl = new MockControl();\n                var mockEditor = mockControl.createMock( {\n                    getSession: function() {},\n                } );\n                var mockSession = mockControl.createMock( {\n                    setAnnotations: function() {},\n                    setMode: function(){}\n                } );\n                var usercodeView = new Dirigible.UsercodeView(mockEditor);\n\n                mockEditor.expects().getSession().andReturn(mockSession);\n                var annotations;\n                mockSession.expects().setAnnotations(TypeOf.isA(Array)).andStub(\n                    function() { annotations = arguments[0]; }\n                );\n                    \n                usercodeView.updateMetaData(\n                    {'usercode_error': {'message': 'user code error', 'line': '3'}}\n                );\n\n                mockControl.verify();\n                assertDeepAreSame(\n                    [{\n                        row: 2,\n                        column: 0,\n                        text: 'user code error',\n                        type: 'error'\n                    }],\n                    annotations\n                );\n            },\n\n            testUpdateMetaDataSafelyClearsAnnotationsWithNoError: function() {\n                var mockControl = new MockControl();\n                var mockEditor = mockControl.createMock( {\n                    getSession: function() {},\n                } );\n                var mockSession = mockControl.createMock( {\n                    setAnnotations: function() {},\n                    setMode: function() {}\n                } );\n\n                var usercodeView = new Dirigible.UsercodeView(mockEditor);\n\n                mockEditor.expects().getSession().andReturn(mockSession);\n                var annotations;\n                mockSession.expects().setAnnotations(TypeOf.isA(Array)).andStub(\n                    function() { annotations = arguments[0]; }\n                );\n                    \n                usercodeView.updateMetaData( {} );\n\n                mockControl.verify();\n                assertDeepAreSame( [], annotations );\n            }\n\n        })\n\n    ];\n</script>\n\n\n<script src=\"yuirunner.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/tests/yuirunner.js",
    "content": "\r\nfunction escape_quotes(key, value) {\r\n    if (typeof value === 'string') {\r\n        // just replacing '<' seems enough to cause json encoding to\r\n        // kick in for '>', etc.\r\n        return value.replace(/\\</g, '&lt;');\r\n    }\r\n    return value;\r\n}\r\n\r\nYAHOO.util.Event.onDOMReady(function () {\r\n    var testRunner = YAHOO.tool.TestRunner;\r\n    for (var i in tests) {\r\n        testRunner.add(tests[i]);\r\n    }\r\n    testRunner.subscribe(\r\n        testRunner.COMPLETE_EVENT,\r\n        function(data) {\r\n            JSON.useNativeStringify = false;\r\n            document.getElementById(\"id_results\").innerHTML = JSON.stringify(data.results, escape_quotes);\r\n        }\r\n    );\r\n    var oLogger = new YAHOO.tool.TestLogger();\r\n    testRunner.run();\r\n});\r\n\r\n"
  },
  {
    "path": "dirigible/shared/static/dirigible/tests/yuitest/yuitest-combo.js",
    "content": "/*\nCopyright (c) 2010, Yahoo! Inc. All rights reserved.\nCode licensed under the BSD License:\nhttp://developer.yahoo.com/yui/license.html\nversion: 2.8.1\n*/\nif(typeof YAHOO==\"undefined\"||!YAHOO){var YAHOO={};}YAHOO.namespace=function(){var A=arguments,E=null,C,B,D;for(C=0;C<A.length;C=C+1){D=(\"\"+A[C]).split(\".\");E=YAHOO;for(B=(D[0]==\"YAHOO\")?1:0;B<D.length;B=B+1){E[D[B]]=E[D[B]]||{};E=E[D[B]];}}return E;};YAHOO.log=function(D,A,C){var B=YAHOO.widget.Logger;if(B&&B.log){return B.log(D,A,C);}else{return false;}};YAHOO.register=function(A,E,D){var I=YAHOO.env.modules,B,H,G,F,C;if(!I[A]){I[A]={versions:[],builds:[]};}B=I[A];H=D.version;G=D.build;F=YAHOO.env.listeners;B.name=A;B.version=H;B.build=G;B.versions.push(H);B.builds.push(G);B.mainClass=E;for(C=0;C<F.length;C=C+1){F[C](B);}if(E){E.VERSION=H;E.BUILD=G;}else{YAHOO.log(\"mainClass is undefined for module \"+A,\"warn\");}};YAHOO.env=YAHOO.env||{modules:[],listeners:[]};YAHOO.env.getVersion=function(A){return YAHOO.env.modules[A]||null;};YAHOO.env.ua=function(){var D=function(H){var I=0;return parseFloat(H.replace(/\\./g,function(){return(I++==1)?\"\":\".\";}));},G=navigator,F={ie:0,opera:0,gecko:0,webkit:0,mobile:null,air:0,caja:G.cajaVersion,secure:false,os:null},C=navigator&&navigator.userAgent,E=window&&window.location,B=E&&E.href,A;F.secure=B&&(B.toLowerCase().indexOf(\"https\")===0);if(C){if((/windows|win32/i).test(C)){F.os=\"windows\";}else{if((/macintosh/i).test(C)){F.os=\"macintosh\";}}if((/KHTML/).test(C)){F.webkit=1;}A=C.match(/AppleWebKit\\/([^\\s]*)/);if(A&&A[1]){F.webkit=D(A[1]);if(/ Mobile\\//.test(C)){F.mobile=\"Apple\";}else{A=C.match(/NokiaN[^\\/]*/);if(A){F.mobile=A[0];}}A=C.match(/AdobeAIR\\/([^\\s]*)/);if(A){F.air=A[0];}}if(!F.webkit){A=C.match(/Opera[\\s\\/]([^\\s]*)/);if(A&&A[1]){F.opera=D(A[1]);A=C.match(/Opera Mini[^;]*/);if(A){F.mobile=A[0];}}else{A=C.match(/MSIE\\s([^;]*)/);if(A&&A[1]){F.ie=D(A[1]);}else{A=C.match(/Gecko\\/([^\\s]*)/);if(A){F.gecko=1;A=C.match(/rv:([^\\s\\)]*)/);if(A&&A[1]){F.gecko=D(A[1]);}}}}}}return F;}();(function(){YAHOO.namespace(\"util\",\"widget\",\"example\");if(\"undefined\"!==typeof YAHOO_config){var B=YAHOO_config.listener,A=YAHOO.env.listeners,D=true,C;if(B){for(C=0;C<A.length;C++){if(A[C]==B){D=false;break;}}if(D){A.push(B);}}}})();YAHOO.lang=YAHOO.lang||{};(function(){var B=YAHOO.lang,A=Object.prototype,H=\"[object Array]\",C=\"[object Function]\",G=\"[object Object]\",E=[],F=[\"toString\",\"valueOf\"],D={isArray:function(I){return A.toString.apply(I)===H;},isBoolean:function(I){return typeof I===\"boolean\";},isFunction:function(I){return(typeof I===\"function\")||A.toString.apply(I)===C;},isNull:function(I){return I===null;},isNumber:function(I){return typeof I===\"number\"&&isFinite(I);},isObject:function(I){return(I&&(typeof I===\"object\"||B.isFunction(I)))||false;},isString:function(I){return typeof I===\"string\";},isUndefined:function(I){return typeof I===\"undefined\";},_IEEnumFix:(YAHOO.env.ua.ie)?function(K,J){var I,M,L;for(I=0;I<F.length;I=I+1){M=F[I];L=J[M];if(B.isFunction(L)&&L!=A[M]){K[M]=L;}}}:function(){},extend:function(L,M,K){if(!M||!L){throw new Error(\"extend failed, please check that \"+\"all dependencies are included.\");}var J=function(){},I;J.prototype=M.prototype;L.prototype=new J();L.prototype.constructor=L;L.superclass=M.prototype;if(M.prototype.constructor==A.constructor){M.prototype.constructor=M;}if(K){for(I in K){if(B.hasOwnProperty(K,I)){L.prototype[I]=K[I];}}B._IEEnumFix(L.prototype,K);}},augmentObject:function(M,L){if(!L||!M){throw new Error(\"Absorb failed, verify dependencies.\");}var I=arguments,K,N,J=I[2];if(J&&J!==true){for(K=2;K<I.length;K=K+1){M[I[K]]=L[I[K]];}}else{for(N in L){if(J||!(N in M)){M[N]=L[N];}}B._IEEnumFix(M,L);}},augmentProto:function(L,K){if(!K||!L){throw new Error(\"Augment failed, verify dependencies.\");}var I=[L.prototype,K.prototype],J;for(J=2;J<arguments.length;J=J+1){I.push(arguments[J]);}B.augmentObject.apply(this,I);},dump:function(I,N){var K,M,P=[],Q=\"{...}\",J=\"f(){...}\",O=\", \",L=\" => \";if(!B.isObject(I)){return I+\"\";}else{if(I instanceof Date||(\"nodeType\" in I&&\"tagName\" in I)){return I;}else{if(B.isFunction(I)){return J;}}}N=(B.isNumber(N))?N:3;if(B.isArray(I)){P.push(\"[\");for(K=0,M=I.length;K<M;K=K+1){if(B.isObject(I[K])){P.push((N>0)?B.dump(I[K],N-1):Q);}else{P.push(I[K]);}P.push(O);}if(P.length>1){P.pop();}P.push(\"]\");}else{P.push(\"{\");for(K in I){if(B.hasOwnProperty(I,K)){P.push(K+L);if(B.isObject(I[K])){P.push((N>0)?B.dump(I[K],N-1):Q);}else{P.push(I[K]);}P.push(O);}}if(P.length>1){P.pop();}P.push(\"}\");}return P.join(\"\");},substitute:function(Y,J,R){var N,M,L,U,V,X,T=[],K,O=\"dump\",S=\" \",I=\"{\",W=\"}\",Q,P;for(;;){N=Y.lastIndexOf(I);if(N<0){break;}M=Y.indexOf(W,N);if(N+1>=M){break;}K=Y.substring(N+1,M);U=K;X=null;L=U.indexOf(S);if(L>-1){X=U.substring(L+1);U=U.substring(0,L);}V=J[U];if(R){V=R(U,V,X);}if(B.isObject(V)){if(B.isArray(V)){V=B.dump(V,parseInt(X,10));}else{X=X||\"\";Q=X.indexOf(O);if(Q>-1){X=X.substring(4);}P=V.toString();if(P===G||Q>-1){V=B.dump(V,parseInt(X,10));}else{V=P;}}}else{if(!B.isString(V)&&!B.isNumber(V)){V=\"~-\"+T.length+\"-~\";T[T.length]=K;}}Y=Y.substring(0,N)+V+Y.substring(M+1);}for(N=T.length-1;N>=0;N=N-1){Y=Y.replace(new RegExp(\"~-\"+N+\"-~\"),\"{\"+T[N]+\"}\",\"g\");}return Y;},trim:function(I){try{return I.replace(/^\\s+|\\s+$/g,\"\");}catch(J){return I;}},merge:function(){var L={},J=arguments,I=J.length,K;for(K=0;K<I;K=K+1){B.augmentObject(L,J[K],true);}return L;},later:function(P,J,Q,L,M){P=P||0;J=J||{};var K=Q,O=L,N,I;if(B.isString(Q)){K=J[Q];}if(!K){throw new TypeError(\"method undefined\");}if(O&&!B.isArray(O)){O=[L];}N=function(){K.apply(J,O||E);};I=(M)?setInterval(N,P):setTimeout(N,P);return{interval:M,cancel:function(){if(this.interval){clearInterval(I);}else{clearTimeout(I);}}};},isValue:function(I){return(B.isObject(I)||B.isString(I)||B.isNumber(I)||B.isBoolean(I));}};B.hasOwnProperty=(A.hasOwnProperty)?function(I,J){return I&&I.hasOwnProperty(J);}:function(I,J){return !B.isUndefined(I[J])&&I.constructor.prototype[J]!==I[J];};D.augmentObject(B,D,true);YAHOO.util.Lang=B;B.augment=B.augmentProto;YAHOO.augment=B.augmentProto;YAHOO.extend=B.extend;})();YAHOO.register(\"yahoo\",YAHOO,{version:\"2.8.1\",build:\"19\"});\n(function(){YAHOO.env._id_counter=YAHOO.env._id_counter||0;var E=YAHOO.util,L=YAHOO.lang,m=YAHOO.env.ua,A=YAHOO.lang.trim,d={},h={},N=/^t(?:able|d|h)$/i,X=/color$/i,K=window.document,W=K.documentElement,e=\"ownerDocument\",n=\"defaultView\",v=\"documentElement\",t=\"compatMode\",b=\"offsetLeft\",P=\"offsetTop\",u=\"offsetParent\",Z=\"parentNode\",l=\"nodeType\",C=\"tagName\",O=\"scrollLeft\",i=\"scrollTop\",Q=\"getBoundingClientRect\",w=\"getComputedStyle\",a=\"currentStyle\",M=\"CSS1Compat\",c=\"BackCompat\",g=\"class\",F=\"className\",J=\"\",B=\" \",s=\"(?:^|\\\\s)\",k=\"(?= |$)\",U=\"g\",p=\"position\",f=\"fixed\",V=\"relative\",j=\"left\",o=\"top\",r=\"medium\",q=\"borderLeftWidth\",R=\"borderTopWidth\",D=m.opera,I=m.webkit,H=m.gecko,T=m.ie;E.Dom={CUSTOM_ATTRIBUTES:(!W.hasAttribute)?{\"for\":\"htmlFor\",\"class\":F}:{\"htmlFor\":\"for\",\"className\":g},DOT_ATTRIBUTES:{},get:function(z){var AB,x,AA,y,Y,G;if(z){if(z[l]||z.item){return z;}if(typeof z===\"string\"){AB=z;z=K.getElementById(z);G=(z)?z.attributes:null;if(z&&G&&G.id&&G.id.value===AB){return z;}else{if(z&&K.all){z=null;x=K.all[AB];for(y=0,Y=x.length;y<Y;++y){if(x[y].id===AB){return x[y];}}}}return z;}if(YAHOO.util.Element&&z instanceof YAHOO.util.Element){z=z.get(\"element\");}if(\"length\" in z){AA=[];for(y=0,Y=z.length;y<Y;++y){AA[AA.length]=E.Dom.get(z[y]);}return AA;}return z;}return null;},getComputedStyle:function(G,Y){if(window[w]){return G[e][n][w](G,null)[Y];}else{if(G[a]){return E.Dom.IE_ComputedStyle.get(G,Y);}}},getStyle:function(G,Y){return E.Dom.batch(G,E.Dom._getStyle,Y);},_getStyle:function(){if(window[w]){return function(G,y){y=(y===\"float\")?y=\"cssFloat\":E.Dom._toCamel(y);var x=G.style[y],Y;if(!x){Y=G[e][n][w](G,null);if(Y){x=Y[y];}}return x;};}else{if(W[a]){return function(G,y){var x;switch(y){case\"opacity\":x=100;try{x=G.filters[\"DXImageTransform.Microsoft.Alpha\"].opacity;}catch(z){try{x=G.filters(\"alpha\").opacity;}catch(Y){}}return x/100;case\"float\":y=\"styleFloat\";default:y=E.Dom._toCamel(y);x=G[a]?G[a][y]:null;return(G.style[y]||x);}};}}}(),setStyle:function(G,Y,x){E.Dom.batch(G,E.Dom._setStyle,{prop:Y,val:x});},_setStyle:function(){if(T){return function(Y,G){var x=E.Dom._toCamel(G.prop),y=G.val;if(Y){switch(x){case\"opacity\":if(L.isString(Y.style.filter)){Y.style.filter=\"alpha(opacity=\"+y*100+\")\";if(!Y[a]||!Y[a].hasLayout){Y.style.zoom=1;}}break;case\"float\":x=\"styleFloat\";default:Y.style[x]=y;}}else{}};}else{return function(Y,G){var x=E.Dom._toCamel(G.prop),y=G.val;if(Y){if(x==\"float\"){x=\"cssFloat\";}Y.style[x]=y;}else{}};}}(),getXY:function(G){return E.Dom.batch(G,E.Dom._getXY);},_canPosition:function(G){return(E.Dom._getStyle(G,\"display\")!==\"none\"&&E.Dom._inDoc(G));},_getXY:function(){if(K[v][Q]){return function(y){var z,Y,AA,AF,AE,AD,AC,G,x,AB=Math.floor,AG=false;if(E.Dom._canPosition(y)){AA=y[Q]();AF=y[e];z=E.Dom.getDocumentScrollLeft(AF);Y=E.Dom.getDocumentScrollTop(AF);AG=[AB(AA[j]),AB(AA[o])];if(T&&m.ie<8){AE=2;AD=2;AC=AF[t];if(m.ie===6){if(AC!==c){AE=0;AD=0;}}if((AC===c)){G=S(AF[v],q);x=S(AF[v],R);if(G!==r){AE=parseInt(G,10);}if(x!==r){AD=parseInt(x,10);}}AG[0]-=AE;AG[1]-=AD;}if((Y||z)){AG[0]+=z;AG[1]+=Y;}AG[0]=AB(AG[0]);AG[1]=AB(AG[1]);}else{}return AG;};}else{return function(y){var x,Y,AA,AB,AC,z=false,G=y;if(E.Dom._canPosition(y)){z=[y[b],y[P]];x=E.Dom.getDocumentScrollLeft(y[e]);Y=E.Dom.getDocumentScrollTop(y[e]);AC=((H||m.webkit>519)?true:false);while((G=G[u])){z[0]+=G[b];z[1]+=G[P];if(AC){z=E.Dom._calcBorders(G,z);}}if(E.Dom._getStyle(y,p)!==f){G=y;while((G=G[Z])&&G[C]){AA=G[i];AB=G[O];if(H&&(E.Dom._getStyle(G,\"overflow\")!==\"visible\")){z=E.Dom._calcBorders(G,z);}if(AA||AB){z[0]-=AB;z[1]-=AA;}}z[0]+=x;z[1]+=Y;}else{if(D){z[0]-=x;z[1]-=Y;}else{if(I||H){z[0]+=x;z[1]+=Y;}}}z[0]=Math.floor(z[0]);z[1]=Math.floor(z[1]);}else{}return z;};}}(),getX:function(G){var Y=function(x){return E.Dom.getXY(x)[0];};return E.Dom.batch(G,Y,E.Dom,true);},getY:function(G){var Y=function(x){return E.Dom.getXY(x)[1];};return E.Dom.batch(G,Y,E.Dom,true);},setXY:function(G,x,Y){E.Dom.batch(G,E.Dom._setXY,{pos:x,noRetry:Y});},_setXY:function(G,z){var AA=E.Dom._getStyle(G,p),y=E.Dom.setStyle,AD=z.pos,Y=z.noRetry,AB=[parseInt(E.Dom.getComputedStyle(G,j),10),parseInt(E.Dom.getComputedStyle(G,o),10)],AC,x;if(AA==\"static\"){AA=V;y(G,p,AA);}AC=E.Dom._getXY(G);if(!AD||AC===false){return false;}if(isNaN(AB[0])){AB[0]=(AA==V)?0:G[b];}if(isNaN(AB[1])){AB[1]=(AA==V)?0:G[P];}if(AD[0]!==null){y(G,j,AD[0]-AC[0]+AB[0]+\"px\");}if(AD[1]!==null){y(G,o,AD[1]-AC[1]+AB[1]+\"px\");}if(!Y){x=E.Dom._getXY(G);if((AD[0]!==null&&x[0]!=AD[0])||(AD[1]!==null&&x[1]!=AD[1])){E.Dom._setXY(G,{pos:AD,noRetry:true});}}},setX:function(Y,G){E.Dom.setXY(Y,[G,null]);},setY:function(G,Y){E.Dom.setXY(G,[null,Y]);},getRegion:function(G){var Y=function(x){var y=false;if(E.Dom._canPosition(x)){y=E.Region.getRegion(x);}else{}return y;};return E.Dom.batch(G,Y,E.Dom,true);},getClientWidth:function(){return E.Dom.getViewportWidth();},getClientHeight:function(){return E.Dom.getViewportHeight();},getElementsByClassName:function(AB,AF,AC,AE,x,AD){AF=AF||\"*\";AC=(AC)?E.Dom.get(AC):null||K;if(!AC){return[];}var Y=[],G=AC.getElementsByTagName(AF),z=E.Dom.hasClass;for(var y=0,AA=G.length;y<AA;++y){if(z(G[y],AB)){Y[Y.length]=G[y];}}if(AE){E.Dom.batch(Y,AE,x,AD);}return Y;},hasClass:function(Y,G){return E.Dom.batch(Y,E.Dom._hasClass,G);},_hasClass:function(x,Y){var G=false,y;if(x&&Y){y=E.Dom._getAttribute(x,F)||J;if(Y.exec){G=Y.test(y);}else{G=Y&&(B+y+B).indexOf(B+Y+B)>-1;}}else{}return G;},addClass:function(Y,G){return E.Dom.batch(Y,E.Dom._addClass,G);},_addClass:function(x,Y){var G=false,y;if(x&&Y){y=E.Dom._getAttribute(x,F)||J;if(!E.Dom._hasClass(x,Y)){E.Dom.setAttribute(x,F,A(y+B+Y));G=true;}}else{}return G;},removeClass:function(Y,G){return E.Dom.batch(Y,E.Dom._removeClass,G);},_removeClass:function(y,x){var Y=false,AA,z,G;if(y&&x){AA=E.Dom._getAttribute(y,F)||J;E.Dom.setAttribute(y,F,AA.replace(E.Dom._getClassRegex(x),J));z=E.Dom._getAttribute(y,F);if(AA!==z){E.Dom.setAttribute(y,F,A(z));Y=true;if(E.Dom._getAttribute(y,F)===\"\"){G=(y.hasAttribute&&y.hasAttribute(g))?g:F;\ny.removeAttribute(G);}}}else{}return Y;},replaceClass:function(x,Y,G){return E.Dom.batch(x,E.Dom._replaceClass,{from:Y,to:G});},_replaceClass:function(y,x){var Y,AB,AA,G=false,z;if(y&&x){AB=x.from;AA=x.to;if(!AA){G=false;}else{if(!AB){G=E.Dom._addClass(y,x.to);}else{if(AB!==AA){z=E.Dom._getAttribute(y,F)||J;Y=(B+z.replace(E.Dom._getClassRegex(AB),B+AA)).split(E.Dom._getClassRegex(AA));Y.splice(1,0,B+AA);E.Dom.setAttribute(y,F,A(Y.join(J)));G=true;}}}}else{}return G;},generateId:function(G,x){x=x||\"yui-gen\";var Y=function(y){if(y&&y.id){return y.id;}var z=x+YAHOO.env._id_counter++;if(y){if(y[e]&&y[e].getElementById(z)){return E.Dom.generateId(y,z+x);}y.id=z;}return z;};return E.Dom.batch(G,Y,E.Dom,true)||Y.apply(E.Dom,arguments);},isAncestor:function(Y,x){Y=E.Dom.get(Y);x=E.Dom.get(x);var G=false;if((Y&&x)&&(Y[l]&&x[l])){if(Y.contains&&Y!==x){G=Y.contains(x);}else{if(Y.compareDocumentPosition){G=!!(Y.compareDocumentPosition(x)&16);}}}else{}return G;},inDocument:function(G,Y){return E.Dom._inDoc(E.Dom.get(G),Y);},_inDoc:function(Y,x){var G=false;if(Y&&Y[C]){x=x||Y[e];G=E.Dom.isAncestor(x[v],Y);}else{}return G;},getElementsBy:function(Y,AF,AB,AD,y,AC,AE){AF=AF||\"*\";AB=(AB)?E.Dom.get(AB):null||K;if(!AB){return[];}var x=[],G=AB.getElementsByTagName(AF);for(var z=0,AA=G.length;z<AA;++z){if(Y(G[z])){if(AE){x=G[z];break;}else{x[x.length]=G[z];}}}if(AD){E.Dom.batch(x,AD,y,AC);}return x;},getElementBy:function(x,G,Y){return E.Dom.getElementsBy(x,G,Y,null,null,null,true);},batch:function(x,AB,AA,z){var y=[],Y=(z)?AA:window;x=(x&&(x[C]||x.item))?x:E.Dom.get(x);if(x&&AB){if(x[C]||x.length===undefined){return AB.call(Y,x,AA);}for(var G=0;G<x.length;++G){y[y.length]=AB.call(Y,x[G],AA);}}else{return false;}return y;},getDocumentHeight:function(){var Y=(K[t]!=M||I)?K.body.scrollHeight:W.scrollHeight,G=Math.max(Y,E.Dom.getViewportHeight());return G;},getDocumentWidth:function(){var Y=(K[t]!=M||I)?K.body.scrollWidth:W.scrollWidth,G=Math.max(Y,E.Dom.getViewportWidth());return G;},getViewportHeight:function(){var G=self.innerHeight,Y=K[t];if((Y||T)&&!D){G=(Y==M)?W.clientHeight:K.body.clientHeight;}return G;},getViewportWidth:function(){var G=self.innerWidth,Y=K[t];if(Y||T){G=(Y==M)?W.clientWidth:K.body.clientWidth;}return G;},getAncestorBy:function(G,Y){while((G=G[Z])){if(E.Dom._testElement(G,Y)){return G;}}return null;},getAncestorByClassName:function(Y,G){Y=E.Dom.get(Y);if(!Y){return null;}var x=function(y){return E.Dom.hasClass(y,G);};return E.Dom.getAncestorBy(Y,x);},getAncestorByTagName:function(Y,G){Y=E.Dom.get(Y);if(!Y){return null;}var x=function(y){return y[C]&&y[C].toUpperCase()==G.toUpperCase();};return E.Dom.getAncestorBy(Y,x);},getPreviousSiblingBy:function(G,Y){while(G){G=G.previousSibling;if(E.Dom._testElement(G,Y)){return G;}}return null;},getPreviousSibling:function(G){G=E.Dom.get(G);if(!G){return null;}return E.Dom.getPreviousSiblingBy(G);},getNextSiblingBy:function(G,Y){while(G){G=G.nextSibling;if(E.Dom._testElement(G,Y)){return G;}}return null;},getNextSibling:function(G){G=E.Dom.get(G);if(!G){return null;}return E.Dom.getNextSiblingBy(G);},getFirstChildBy:function(G,x){var Y=(E.Dom._testElement(G.firstChild,x))?G.firstChild:null;return Y||E.Dom.getNextSiblingBy(G.firstChild,x);},getFirstChild:function(G,Y){G=E.Dom.get(G);if(!G){return null;}return E.Dom.getFirstChildBy(G);},getLastChildBy:function(G,x){if(!G){return null;}var Y=(E.Dom._testElement(G.lastChild,x))?G.lastChild:null;return Y||E.Dom.getPreviousSiblingBy(G.lastChild,x);},getLastChild:function(G){G=E.Dom.get(G);return E.Dom.getLastChildBy(G);},getChildrenBy:function(Y,y){var x=E.Dom.getFirstChildBy(Y,y),G=x?[x]:[];E.Dom.getNextSiblingBy(x,function(z){if(!y||y(z)){G[G.length]=z;}return false;});return G;},getChildren:function(G){G=E.Dom.get(G);if(!G){}return E.Dom.getChildrenBy(G);},getDocumentScrollLeft:function(G){G=G||K;return Math.max(G[v].scrollLeft,G.body.scrollLeft);},getDocumentScrollTop:function(G){G=G||K;return Math.max(G[v].scrollTop,G.body.scrollTop);},insertBefore:function(Y,G){Y=E.Dom.get(Y);G=E.Dom.get(G);if(!Y||!G||!G[Z]){return null;}return G[Z].insertBefore(Y,G);},insertAfter:function(Y,G){Y=E.Dom.get(Y);G=E.Dom.get(G);if(!Y||!G||!G[Z]){return null;}if(G.nextSibling){return G[Z].insertBefore(Y,G.nextSibling);}else{return G[Z].appendChild(Y);}},getClientRegion:function(){var x=E.Dom.getDocumentScrollTop(),Y=E.Dom.getDocumentScrollLeft(),y=E.Dom.getViewportWidth()+Y,G=E.Dom.getViewportHeight()+x;return new E.Region(x,y,G,Y);},setAttribute:function(Y,G,x){E.Dom.batch(Y,E.Dom._setAttribute,{attr:G,val:x});},_setAttribute:function(x,Y){var G=E.Dom._toCamel(Y.attr),y=Y.val;if(x&&x.setAttribute){if(E.Dom.DOT_ATTRIBUTES[G]){x[G]=y;}else{G=E.Dom.CUSTOM_ATTRIBUTES[G]||G;x.setAttribute(G,y);}}else{}},getAttribute:function(Y,G){return E.Dom.batch(Y,E.Dom._getAttribute,G);},_getAttribute:function(Y,G){var x;G=E.Dom.CUSTOM_ATTRIBUTES[G]||G;if(Y&&Y.getAttribute){x=Y.getAttribute(G,2);}else{}return x;},_toCamel:function(Y){var x=d;function G(y,z){return z.toUpperCase();}return x[Y]||(x[Y]=Y.indexOf(\"-\")===-1?Y:Y.replace(/-([a-z])/gi,G));},_getClassRegex:function(Y){var G;if(Y!==undefined){if(Y.exec){G=Y;}else{G=h[Y];if(!G){Y=Y.replace(E.Dom._patterns.CLASS_RE_TOKENS,\"\\\\$1\");G=h[Y]=new RegExp(s+Y+k,U);}}}return G;},_patterns:{ROOT_TAG:/^body|html$/i,CLASS_RE_TOKENS:/([\\.\\(\\)\\^\\$\\*\\+\\?\\|\\[\\]\\{\\}\\\\])/g},_testElement:function(G,Y){return G&&G[l]==1&&(!Y||Y(G));},_calcBorders:function(x,y){var Y=parseInt(E.Dom[w](x,R),10)||0,G=parseInt(E.Dom[w](x,q),10)||0;if(H){if(N.test(x[C])){Y=0;G=0;}}y[0]+=G;y[1]+=Y;return y;}};var S=E.Dom[w];if(m.opera){E.Dom[w]=function(Y,G){var x=S(Y,G);if(X.test(G)){x=E.Dom.Color.toRGB(x);}return x;};}if(m.webkit){E.Dom[w]=function(Y,G){var x=S(Y,G);if(x===\"rgba(0, 0, 0, 0)\"){x=\"transparent\";}return x;};}if(m.ie&&m.ie>=8&&K.documentElement.hasAttribute){E.Dom.DOT_ATTRIBUTES.type=true;}})();YAHOO.util.Region=function(C,D,A,B){this.top=C;this.y=C;this[1]=C;this.right=D;this.bottom=A;this.left=B;this.x=B;this[0]=B;\nthis.width=this.right-this.left;this.height=this.bottom-this.top;};YAHOO.util.Region.prototype.contains=function(A){return(A.left>=this.left&&A.right<=this.right&&A.top>=this.top&&A.bottom<=this.bottom);};YAHOO.util.Region.prototype.getArea=function(){return((this.bottom-this.top)*(this.right-this.left));};YAHOO.util.Region.prototype.intersect=function(E){var C=Math.max(this.top,E.top),D=Math.min(this.right,E.right),A=Math.min(this.bottom,E.bottom),B=Math.max(this.left,E.left);if(A>=C&&D>=B){return new YAHOO.util.Region(C,D,A,B);}else{return null;}};YAHOO.util.Region.prototype.union=function(E){var C=Math.min(this.top,E.top),D=Math.max(this.right,E.right),A=Math.max(this.bottom,E.bottom),B=Math.min(this.left,E.left);return new YAHOO.util.Region(C,D,A,B);};YAHOO.util.Region.prototype.toString=function(){return(\"Region {\"+\"top: \"+this.top+\", right: \"+this.right+\", bottom: \"+this.bottom+\", left: \"+this.left+\", height: \"+this.height+\", width: \"+this.width+\"}\");};YAHOO.util.Region.getRegion=function(D){var F=YAHOO.util.Dom.getXY(D),C=F[1],E=F[0]+D.offsetWidth,A=F[1]+D.offsetHeight,B=F[0];return new YAHOO.util.Region(C,E,A,B);};YAHOO.util.Point=function(A,B){if(YAHOO.lang.isArray(A)){B=A[1];A=A[0];}YAHOO.util.Point.superclass.constructor.call(this,B,A,B,A);};YAHOO.extend(YAHOO.util.Point,YAHOO.util.Region);(function(){var B=YAHOO.util,A=\"clientTop\",F=\"clientLeft\",J=\"parentNode\",K=\"right\",W=\"hasLayout\",I=\"px\",U=\"opacity\",L=\"auto\",D=\"borderLeftWidth\",G=\"borderTopWidth\",P=\"borderRightWidth\",V=\"borderBottomWidth\",S=\"visible\",Q=\"transparent\",N=\"height\",E=\"width\",H=\"style\",T=\"currentStyle\",R=/^width|height$/,O=/^(\\d[.\\d]*)+(em|ex|px|gd|rem|vw|vh|vm|ch|mm|cm|in|pt|pc|deg|rad|ms|s|hz|khz|%){1}?/i,M={get:function(X,Z){var Y=\"\",a=X[T][Z];if(Z===U){Y=B.Dom.getStyle(X,U);}else{if(!a||(a.indexOf&&a.indexOf(I)>-1)){Y=a;}else{if(B.Dom.IE_COMPUTED[Z]){Y=B.Dom.IE_COMPUTED[Z](X,Z);}else{if(O.test(a)){Y=B.Dom.IE.ComputedStyle.getPixel(X,Z);}else{Y=a;}}}}return Y;},getOffset:function(Z,e){var b=Z[T][e],X=e.charAt(0).toUpperCase()+e.substr(1),c=\"offset\"+X,Y=\"pixel\"+X,a=\"\",d;if(b==L){d=Z[c];if(d===undefined){a=0;}a=d;if(R.test(e)){Z[H][e]=d;if(Z[c]>d){a=d-(Z[c]-d);}Z[H][e]=L;}}else{if(!Z[H][Y]&&!Z[H][e]){Z[H][e]=b;}a=Z[H][Y];}return a+I;},getBorderWidth:function(X,Z){var Y=null;if(!X[T][W]){X[H].zoom=1;}switch(Z){case G:Y=X[A];break;case V:Y=X.offsetHeight-X.clientHeight-X[A];break;case D:Y=X[F];break;case P:Y=X.offsetWidth-X.clientWidth-X[F];break;}return Y+I;},getPixel:function(Y,X){var a=null,b=Y[T][K],Z=Y[T][X];Y[H][K]=Z;a=Y[H].pixelRight;Y[H][K]=b;return a+I;},getMargin:function(Y,X){var Z;if(Y[T][X]==L){Z=0+I;}else{Z=B.Dom.IE.ComputedStyle.getPixel(Y,X);}return Z;},getVisibility:function(Y,X){var Z;while((Z=Y[T])&&Z[X]==\"inherit\"){Y=Y[J];}return(Z)?Z[X]:S;},getColor:function(Y,X){return B.Dom.Color.toRGB(Y[T][X])||Q;},getBorderColor:function(Y,X){var Z=Y[T],a=Z[X]||Z.color;return B.Dom.Color.toRGB(B.Dom.Color.toHex(a));}},C={};C.top=C.right=C.bottom=C.left=C[E]=C[N]=M.getOffset;C.color=M.getColor;C[G]=C[P]=C[V]=C[D]=M.getBorderWidth;C.marginTop=C.marginRight=C.marginBottom=C.marginLeft=M.getMargin;C.visibility=M.getVisibility;C.borderColor=C.borderTopColor=C.borderRightColor=C.borderBottomColor=C.borderLeftColor=M.getBorderColor;B.Dom.IE_COMPUTED=C;B.Dom.IE_ComputedStyle=M;})();(function(){var C=\"toString\",A=parseInt,B=RegExp,D=YAHOO.util;D.Dom.Color={KEYWORDS:{black:\"000\",silver:\"c0c0c0\",gray:\"808080\",white:\"fff\",maroon:\"800000\",red:\"f00\",purple:\"800080\",fuchsia:\"f0f\",green:\"008000\",lime:\"0f0\",olive:\"808000\",yellow:\"ff0\",navy:\"000080\",blue:\"00f\",teal:\"008080\",aqua:\"0ff\"},re_RGB:/^rgb\\(([0-9]+)\\s*,\\s*([0-9]+)\\s*,\\s*([0-9]+)\\)$/i,re_hex:/^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i,re_hex3:/([0-9A-F])/gi,toRGB:function(E){if(!D.Dom.Color.re_RGB.test(E)){E=D.Dom.Color.toHex(E);}if(D.Dom.Color.re_hex.exec(E)){E=\"rgb(\"+[A(B.$1,16),A(B.$2,16),A(B.$3,16)].join(\", \")+\")\";}return E;},toHex:function(H){H=D.Dom.Color.KEYWORDS[H]||H;if(D.Dom.Color.re_RGB.exec(H)){var G=(B.$1.length===1)?\"0\"+B.$1:Number(B.$1),F=(B.$2.length===1)?\"0\"+B.$2:Number(B.$2),E=(B.$3.length===1)?\"0\"+B.$3:Number(B.$3);H=[G[C](16),F[C](16),E[C](16)].join(\"\");}if(H.length<6){H=H.replace(D.Dom.Color.re_hex3,\"$1$1\");}if(H!==\"transparent\"&&H.indexOf(\"#\")<0){H=\"#\"+H;}return H.toLowerCase();}};}());YAHOO.register(\"dom\",YAHOO.util.Dom,{version:\"2.8.1\",build:\"19\"});YAHOO.util.CustomEvent=function(D,C,B,A,E){this.type=D;this.scope=C||window;this.silent=B;this.fireOnce=E;this.fired=false;this.firedWith=null;this.signature=A||YAHOO.util.CustomEvent.LIST;this.subscribers=[];if(!this.silent){}var F=\"_YUICEOnSubscribe\";if(D!==F){this.subscribeEvent=new YAHOO.util.CustomEvent(F,this,true);}this.lastError=null;};YAHOO.util.CustomEvent.LIST=0;YAHOO.util.CustomEvent.FLAT=1;YAHOO.util.CustomEvent.prototype={subscribe:function(B,C,D){if(!B){throw new Error(\"Invalid callback for subscriber to '\"+this.type+\"'\");}if(this.subscribeEvent){this.subscribeEvent.fire(B,C,D);}var A=new YAHOO.util.Subscriber(B,C,D);if(this.fireOnce&&this.fired){this.notify(A,this.firedWith);}else{this.subscribers.push(A);}},unsubscribe:function(D,F){if(!D){return this.unsubscribeAll();}var E=false;for(var B=0,A=this.subscribers.length;B<A;++B){var C=this.subscribers[B];if(C&&C.contains(D,F)){this._delete(B);E=true;}}return E;},fire:function(){this.lastError=null;var H=[],A=this.subscribers.length;var D=[].slice.call(arguments,0),C=true,F,B=false;if(this.fireOnce){if(this.fired){return true;}else{this.firedWith=D;}}this.fired=true;if(!A&&this.silent){return true;}if(!this.silent){}var E=this.subscribers.slice();for(F=0;F<A;++F){var G=E[F];if(!G){B=true;}else{C=this.notify(G,D);if(false===C){if(!this.silent){}break;}}}return(C!==false);},notify:function(F,C){var B,H=null,E=F.getScope(this.scope),A=YAHOO.util.Event.throwErrors;if(!this.silent){}if(this.signature==YAHOO.util.CustomEvent.FLAT){if(C.length>0){H=C[0];}try{B=F.fn.call(E,H,F.obj);}catch(G){this.lastError=G;if(A){throw G;}}}else{try{B=F.fn.call(E,this.type,C,F.obj);}catch(D){this.lastError=D;if(A){throw D;}}}return B;},unsubscribeAll:function(){var A=this.subscribers.length,B;for(B=A-1;B>-1;B--){this._delete(B);}this.subscribers=[];return A;},_delete:function(A){var B=this.subscribers[A];if(B){delete B.fn;delete B.obj;}this.subscribers.splice(A,1);},toString:function(){return\"CustomEvent: \"+\"'\"+this.type+\"', \"+\"context: \"+this.scope;}};YAHOO.util.Subscriber=function(A,B,C){this.fn=A;this.obj=YAHOO.lang.isUndefined(B)?null:B;this.overrideContext=C;};YAHOO.util.Subscriber.prototype.getScope=function(A){if(this.overrideContext){if(this.overrideContext===true){return this.obj;}else{return this.overrideContext;}}return A;};YAHOO.util.Subscriber.prototype.contains=function(A,B){if(B){return(this.fn==A&&this.obj==B);}else{return(this.fn==A);}};YAHOO.util.Subscriber.prototype.toString=function(){return\"Subscriber { obj: \"+this.obj+\", overrideContext: \"+(this.overrideContext||\"no\")+\" }\";};if(!YAHOO.util.Event){YAHOO.util.Event=function(){var G=false,H=[],J=[],A=0,E=[],B=0,C={63232:38,63233:40,63234:37,63235:39,63276:33,63277:34,25:9},D=YAHOO.env.ua.ie,F=\"focusin\",I=\"focusout\";return{POLL_RETRYS:500,POLL_INTERVAL:40,EL:0,TYPE:1,FN:2,WFN:3,UNLOAD_OBJ:3,ADJ_SCOPE:4,OBJ:5,OVERRIDE:6,CAPTURE:7,lastError:null,isSafari:YAHOO.env.ua.webkit,webkit:YAHOO.env.ua.webkit,isIE:D,_interval:null,_dri:null,_specialTypes:{focusin:(D?\"focusin\":\"focus\"),focusout:(D?\"focusout\":\"blur\")},DOMReady:false,throwErrors:false,startInterval:function(){if(!this._interval){this._interval=YAHOO.lang.later(this.POLL_INTERVAL,this,this._tryPreloadAttach,null,true);}},onAvailable:function(Q,M,O,P,N){var K=(YAHOO.lang.isString(Q))?[Q]:Q;for(var L=0;L<K.length;L=L+1){E.push({id:K[L],fn:M,obj:O,overrideContext:P,checkReady:N});}A=this.POLL_RETRYS;this.startInterval();},onContentReady:function(N,K,L,M){this.onAvailable(N,K,L,M,true);},onDOMReady:function(){this.DOMReadyEvent.subscribe.apply(this.DOMReadyEvent,arguments);},_addListener:function(M,K,V,P,T,Y){if(!V||!V.call){return false;}if(this._isValidCollection(M)){var W=true;for(var Q=0,S=M.length;Q<S;++Q){W=this.on(M[Q],K,V,P,T)&&W;}return W;}else{if(YAHOO.lang.isString(M)){var O=this.getEl(M);if(O){M=O;}else{this.onAvailable(M,function(){YAHOO.util.Event._addListener(M,K,V,P,T,Y);});return true;}}}if(!M){return false;}if(\"unload\"==K&&P!==this){J[J.length]=[M,K,V,P,T];return true;}var L=M;if(T){if(T===true){L=P;}else{L=T;}}var N=function(Z){return V.call(L,YAHOO.util.Event.getEvent(Z,M),P);};var X=[M,K,V,N,L,P,T,Y];var R=H.length;H[R]=X;try{this._simpleAdd(M,K,N,Y);}catch(U){this.lastError=U;this.removeListener(M,K,V);return false;}return true;},_getType:function(K){return this._specialTypes[K]||K;},addListener:function(M,P,L,N,O){var K=((P==F||P==I)&&!YAHOO.env.ua.ie)?true:false;return this._addListener(M,this._getType(P),L,N,O,K);},addFocusListener:function(L,K,M,N){return this.on(L,F,K,M,N);},removeFocusListener:function(L,K){return this.removeListener(L,F,K);},addBlurListener:function(L,K,M,N){return this.on(L,I,K,M,N);},removeBlurListener:function(L,K){return this.removeListener(L,I,K);},removeListener:function(L,K,R){var M,P,U;K=this._getType(K);if(typeof L==\"string\"){L=this.getEl(L);}else{if(this._isValidCollection(L)){var S=true;for(M=L.length-1;M>-1;M--){S=(this.removeListener(L[M],K,R)&&S);}return S;}}if(!R||!R.call){return this.purgeElement(L,false,K);}if(\"unload\"==K){for(M=J.length-1;M>-1;M--){U=J[M];if(U&&U[0]==L&&U[1]==K&&U[2]==R){J.splice(M,1);return true;}}return false;}var N=null;var O=arguments[3];if(\"undefined\"===typeof O){O=this._getCacheIndex(H,L,K,R);}if(O>=0){N=H[O];}if(!L||!N){return false;}var T=N[this.CAPTURE]===true?true:false;try{this._simpleRemove(L,K,N[this.WFN],T);}catch(Q){this.lastError=Q;return false;}delete H[O][this.WFN];delete H[O][this.FN];H.splice(O,1);return true;},getTarget:function(M,L){var K=M.target||M.srcElement;return this.resolveTextNode(K);},resolveTextNode:function(L){try{if(L&&3==L.nodeType){return L.parentNode;}}catch(K){}return L;},getPageX:function(L){var K=L.pageX;if(!K&&0!==K){K=L.clientX||0;if(this.isIE){K+=this._getScrollLeft();}}return K;},getPageY:function(K){var L=K.pageY;if(!L&&0!==L){L=K.clientY||0;if(this.isIE){L+=this._getScrollTop();}}return L;},getXY:function(K){return[this.getPageX(K),this.getPageY(K)];},getRelatedTarget:function(L){var K=L.relatedTarget;if(!K){if(L.type==\"mouseout\"){K=L.toElement;\n}else{if(L.type==\"mouseover\"){K=L.fromElement;}}}return this.resolveTextNode(K);},getTime:function(M){if(!M.time){var L=new Date().getTime();try{M.time=L;}catch(K){this.lastError=K;return L;}}return M.time;},stopEvent:function(K){this.stopPropagation(K);this.preventDefault(K);},stopPropagation:function(K){if(K.stopPropagation){K.stopPropagation();}else{K.cancelBubble=true;}},preventDefault:function(K){if(K.preventDefault){K.preventDefault();}else{K.returnValue=false;}},getEvent:function(M,K){var L=M||window.event;if(!L){var N=this.getEvent.caller;while(N){L=N.arguments[0];if(L&&Event==L.constructor){break;}N=N.caller;}}return L;},getCharCode:function(L){var K=L.keyCode||L.charCode||0;if(YAHOO.env.ua.webkit&&(K in C)){K=C[K];}return K;},_getCacheIndex:function(M,P,Q,O){for(var N=0,L=M.length;N<L;N=N+1){var K=M[N];if(K&&K[this.FN]==O&&K[this.EL]==P&&K[this.TYPE]==Q){return N;}}return -1;},generateId:function(K){var L=K.id;if(!L){L=\"yuievtautoid-\"+B;++B;K.id=L;}return L;},_isValidCollection:function(L){try{return(L&&typeof L!==\"string\"&&L.length&&!L.tagName&&!L.alert&&typeof L[0]!==\"undefined\");}catch(K){return false;}},elCache:{},getEl:function(K){return(typeof K===\"string\")?document.getElementById(K):K;},clearCache:function(){},DOMReadyEvent:new YAHOO.util.CustomEvent(\"DOMReady\",YAHOO,0,0,1),_load:function(L){if(!G){G=true;var K=YAHOO.util.Event;K._ready();K._tryPreloadAttach();}},_ready:function(L){var K=YAHOO.util.Event;if(!K.DOMReady){K.DOMReady=true;K.DOMReadyEvent.fire();K._simpleRemove(document,\"DOMContentLoaded\",K._ready);}},_tryPreloadAttach:function(){if(E.length===0){A=0;if(this._interval){this._interval.cancel();this._interval=null;}return;}if(this.locked){return;}if(this.isIE){if(!this.DOMReady){this.startInterval();return;}}this.locked=true;var Q=!G;if(!Q){Q=(A>0&&E.length>0);}var P=[];var R=function(T,U){var S=T;if(U.overrideContext){if(U.overrideContext===true){S=U.obj;}else{S=U.overrideContext;}}U.fn.call(S,U.obj);};var L,K,O,N,M=[];for(L=0,K=E.length;L<K;L=L+1){O=E[L];if(O){N=this.getEl(O.id);if(N){if(O.checkReady){if(G||N.nextSibling||!Q){M.push(O);E[L]=null;}}else{R(N,O);E[L]=null;}}else{P.push(O);}}}for(L=0,K=M.length;L<K;L=L+1){O=M[L];R(this.getEl(O.id),O);}A--;if(Q){for(L=E.length-1;L>-1;L--){O=E[L];if(!O||!O.id){E.splice(L,1);}}this.startInterval();}else{if(this._interval){this._interval.cancel();this._interval=null;}}this.locked=false;},purgeElement:function(O,P,R){var M=(YAHOO.lang.isString(O))?this.getEl(O):O;var Q=this.getListeners(M,R),N,K;if(Q){for(N=Q.length-1;N>-1;N--){var L=Q[N];this.removeListener(M,L.type,L.fn);}}if(P&&M&&M.childNodes){for(N=0,K=M.childNodes.length;N<K;++N){this.purgeElement(M.childNodes[N],P,R);}}},getListeners:function(M,K){var P=[],L;if(!K){L=[H,J];}else{if(K===\"unload\"){L=[J];}else{K=this._getType(K);L=[H];}}var R=(YAHOO.lang.isString(M))?this.getEl(M):M;for(var O=0;O<L.length;O=O+1){var T=L[O];if(T){for(var Q=0,S=T.length;Q<S;++Q){var N=T[Q];if(N&&N[this.EL]===R&&(!K||K===N[this.TYPE])){P.push({type:N[this.TYPE],fn:N[this.FN],obj:N[this.OBJ],adjust:N[this.OVERRIDE],scope:N[this.ADJ_SCOPE],index:Q});}}}}return(P.length)?P:null;},_unload:function(R){var L=YAHOO.util.Event,O,N,M,Q,P,S=J.slice(),K;for(O=0,Q=J.length;O<Q;++O){M=S[O];if(M){K=window;if(M[L.ADJ_SCOPE]){if(M[L.ADJ_SCOPE]===true){K=M[L.UNLOAD_OBJ];}else{K=M[L.ADJ_SCOPE];}}M[L.FN].call(K,L.getEvent(R,M[L.EL]),M[L.UNLOAD_OBJ]);S[O]=null;}}M=null;K=null;J=null;if(H){for(N=H.length-1;N>-1;N--){M=H[N];if(M){L.removeListener(M[L.EL],M[L.TYPE],M[L.FN],N);}}M=null;}L._simpleRemove(window,\"unload\",L._unload);},_getScrollLeft:function(){return this._getScroll()[1];},_getScrollTop:function(){return this._getScroll()[0];},_getScroll:function(){var K=document.documentElement,L=document.body;if(K&&(K.scrollTop||K.scrollLeft)){return[K.scrollTop,K.scrollLeft];}else{if(L){return[L.scrollTop,L.scrollLeft];}else{return[0,0];}}},regCE:function(){},_simpleAdd:function(){if(window.addEventListener){return function(M,N,L,K){M.addEventListener(N,L,(K));};}else{if(window.attachEvent){return function(M,N,L,K){M.attachEvent(\"on\"+N,L);};}else{return function(){};}}}(),_simpleRemove:function(){if(window.removeEventListener){return function(M,N,L,K){M.removeEventListener(N,L,(K));};}else{if(window.detachEvent){return function(L,M,K){L.detachEvent(\"on\"+M,K);};}else{return function(){};}}}()};}();(function(){var EU=YAHOO.util.Event;EU.on=EU.addListener;EU.onFocus=EU.addFocusListener;EU.onBlur=EU.addBlurListener;\n/* DOMReady: based on work by: Dean Edwards/John Resig/Matthias Miller/Diego Perini */\nif(EU.isIE){if(self!==self.top){document.onreadystatechange=function(){if(document.readyState==\"complete\"){document.onreadystatechange=null;EU._ready();}};}else{YAHOO.util.Event.onDOMReady(YAHOO.util.Event._tryPreloadAttach,YAHOO.util.Event,true);var n=document.createElement(\"p\");EU._dri=setInterval(function(){try{n.doScroll(\"left\");clearInterval(EU._dri);EU._dri=null;EU._ready();n=null;}catch(ex){}},EU.POLL_INTERVAL);}}else{if(EU.webkit&&EU.webkit<525){EU._dri=setInterval(function(){var rs=document.readyState;if(\"loaded\"==rs||\"complete\"==rs){clearInterval(EU._dri);EU._dri=null;EU._ready();}},EU.POLL_INTERVAL);}else{EU._simpleAdd(document,\"DOMContentLoaded\",EU._ready);}}EU._simpleAdd(window,\"load\",EU._load);EU._simpleAdd(window,\"unload\",EU._unload);EU._tryPreloadAttach();})();}YAHOO.util.EventProvider=function(){};YAHOO.util.EventProvider.prototype={__yui_events:null,__yui_subscribers:null,subscribe:function(A,C,F,E){this.__yui_events=this.__yui_events||{};var D=this.__yui_events[A];if(D){D.subscribe(C,F,E);}else{this.__yui_subscribers=this.__yui_subscribers||{};var B=this.__yui_subscribers;if(!B[A]){B[A]=[];}B[A].push({fn:C,obj:F,overrideContext:E});}},unsubscribe:function(C,E,G){this.__yui_events=this.__yui_events||{};var A=this.__yui_events;if(C){var F=A[C];if(F){return F.unsubscribe(E,G);}}else{var B=true;for(var D in A){if(YAHOO.lang.hasOwnProperty(A,D)){B=B&&A[D].unsubscribe(E,G);}}return B;}return false;},unsubscribeAll:function(A){return this.unsubscribe(A);\n},createEvent:function(B,G){this.__yui_events=this.__yui_events||{};var E=G||{},D=this.__yui_events,F;if(D[B]){}else{F=new YAHOO.util.CustomEvent(B,E.scope||this,E.silent,YAHOO.util.CustomEvent.FLAT,E.fireOnce);D[B]=F;if(E.onSubscribeCallback){F.subscribeEvent.subscribe(E.onSubscribeCallback);}this.__yui_subscribers=this.__yui_subscribers||{};var A=this.__yui_subscribers[B];if(A){for(var C=0;C<A.length;++C){F.subscribe(A[C].fn,A[C].obj,A[C].overrideContext);}}}return D[B];},fireEvent:function(B){this.__yui_events=this.__yui_events||{};var D=this.__yui_events[B];if(!D){return null;}var A=[];for(var C=1;C<arguments.length;++C){A.push(arguments[C]);}return D.fire.apply(D,A);},hasEvent:function(A){if(this.__yui_events){if(this.__yui_events[A]){return true;}}return false;}};(function(){var A=YAHOO.util.Event,C=YAHOO.lang;YAHOO.util.KeyListener=function(D,I,E,F){if(!D){}else{if(!I){}else{if(!E){}}}if(!F){F=YAHOO.util.KeyListener.KEYDOWN;}var G=new YAHOO.util.CustomEvent(\"keyPressed\");this.enabledEvent=new YAHOO.util.CustomEvent(\"enabled\");this.disabledEvent=new YAHOO.util.CustomEvent(\"disabled\");if(C.isString(D)){D=document.getElementById(D);}if(C.isFunction(E)){G.subscribe(E);}else{G.subscribe(E.fn,E.scope,E.correctScope);}function H(O,N){if(!I.shift){I.shift=false;}if(!I.alt){I.alt=false;}if(!I.ctrl){I.ctrl=false;}if(O.shiftKey==I.shift&&O.altKey==I.alt&&O.ctrlKey==I.ctrl){var J,M=I.keys,L;if(YAHOO.lang.isArray(M)){for(var K=0;K<M.length;K++){J=M[K];L=A.getCharCode(O);if(J==L){G.fire(L,O);break;}}}else{L=A.getCharCode(O);if(M==L){G.fire(L,O);}}}}this.enable=function(){if(!this.enabled){A.on(D,F,H);this.enabledEvent.fire(I);}this.enabled=true;};this.disable=function(){if(this.enabled){A.removeListener(D,F,H);this.disabledEvent.fire(I);}this.enabled=false;};this.toString=function(){return\"KeyListener [\"+I.keys+\"] \"+D.tagName+(D.id?\"[\"+D.id+\"]\":\"\");};};var B=YAHOO.util.KeyListener;B.KEYDOWN=\"keydown\";B.KEYUP=\"keyup\";B.KEY={ALT:18,BACK_SPACE:8,CAPS_LOCK:20,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,META:224,NUM_LOCK:144,PAGE_DOWN:34,PAGE_UP:33,PAUSE:19,PRINTSCREEN:44,RIGHT:39,SCROLL_LOCK:145,SHIFT:16,SPACE:32,TAB:9,UP:38};})();YAHOO.register(\"event\",YAHOO.util.Event,{version:\"2.8.1\",build:\"19\"});YAHOO.register(\"yahoo-dom-event\", YAHOO, {version: \"2.8.1\", build: \"19\"});/*\nCopyright (c) 2010, Yahoo! Inc. All rights reserved.\nCode licensed under the BSD License:\nhttp://developer.yahoo.com/yui/license.html\nversion: 2.8.1\n*/\nYAHOO.widget.LogMsg=function(A){this.msg=this.time=this.category=this.source=this.sourceDetail=null;if(A&&(A.constructor==Object)){for(var B in A){if(A.hasOwnProperty(B)){this[B]=A[B];}}}};YAHOO.widget.LogWriter=function(A){if(!A){YAHOO.log(\"Could not instantiate LogWriter due to invalid source.\",\"error\",\"LogWriter\");return;}this._source=A;};YAHOO.widget.LogWriter.prototype.toString=function(){return\"LogWriter \"+this._sSource;};YAHOO.widget.LogWriter.prototype.log=function(A,B){YAHOO.widget.Logger.log(A,B,this._source);};YAHOO.widget.LogWriter.prototype.getSource=function(){return this._source;};YAHOO.widget.LogWriter.prototype.setSource=function(A){if(!A){YAHOO.log(\"Could not set source due to invalid source.\",\"error\",this.toString());return;}else{this._source=A;}};YAHOO.widget.LogWriter.prototype._source=null;if(!YAHOO.widget.Logger){YAHOO.widget.Logger={loggerEnabled:true,_browserConsoleEnabled:false,categories:[\"info\",\"warn\",\"error\",\"time\",\"window\"],sources:[\"global\"],_stack:[],maxStackEntries:2500,_startTime:new Date().getTime(),_lastTime:null,_windowErrorsHandled:false,_origOnWindowError:null};YAHOO.widget.Logger.log=function(B,F,G){if(this.loggerEnabled){if(!F){F=\"info\";}else{F=F.toLocaleLowerCase();if(this._isNewCategory(F)){this._createNewCategory(F);}}var C=\"global\";var A=null;if(G){var D=G.indexOf(\" \");if(D>0){C=G.substring(0,D);A=G.substring(D,G.length);}else{C=G;}if(this._isNewSource(C)){this._createNewSource(C);}}var H=new Date();var J=new YAHOO.widget.LogMsg({msg:B,time:H,category:F,source:C,sourceDetail:A});var I=this._stack;var E=this.maxStackEntries;if(E&&!isNaN(E)&&(I.length>=E)){I.shift();}I.push(J);this.newLogEvent.fire(J);if(this._browserConsoleEnabled){this._printToBrowserConsole(J);}return true;}else{return false;}};YAHOO.widget.Logger.reset=function(){this._stack=[];this._startTime=new Date().getTime();this.loggerEnabled=true;this.log(\"Logger reset\");this.logResetEvent.fire();};YAHOO.widget.Logger.getStack=function(){return this._stack;};YAHOO.widget.Logger.getStartTime=function(){return this._startTime;};YAHOO.widget.Logger.disableBrowserConsole=function(){YAHOO.log(\"Logger output to the function console.log() has been disabled.\");this._browserConsoleEnabled=false;};YAHOO.widget.Logger.enableBrowserConsole=function(){this._browserConsoleEnabled=true;YAHOO.log(\"Logger output to the function console.log() has been enabled.\");};YAHOO.widget.Logger.handleWindowErrors=function(){if(!YAHOO.widget.Logger._windowErrorsHandled){if(window.error){YAHOO.widget.Logger._origOnWindowError=window.onerror;}window.onerror=YAHOO.widget.Logger._onWindowError;YAHOO.widget.Logger._windowErrorsHandled=true;YAHOO.log(\"Logger handling of window.onerror has been enabled.\");}else{YAHOO.log(\"Logger handling of window.onerror had already been enabled.\");}};YAHOO.widget.Logger.unhandleWindowErrors=function(){if(YAHOO.widget.Logger._windowErrorsHandled){if(YAHOO.widget.Logger._origOnWindowError){window.onerror=YAHOO.widget.Logger._origOnWindowError;YAHOO.widget.Logger._origOnWindowError=null;}else{window.onerror=null;}YAHOO.widget.Logger._windowErrorsHandled=false;YAHOO.log(\"Logger handling of window.onerror has been disabled.\");}else{YAHOO.log(\"Logger handling of window.onerror had already been disabled.\");}};YAHOO.widget.Logger.categoryCreateEvent=new YAHOO.util.CustomEvent(\"categoryCreate\",this,true);YAHOO.widget.Logger.sourceCreateEvent=new YAHOO.util.CustomEvent(\"sourceCreate\",this,true);YAHOO.widget.Logger.newLogEvent=new YAHOO.util.CustomEvent(\"newLog\",this,true);YAHOO.widget.Logger.logResetEvent=new YAHOO.util.CustomEvent(\"logReset\",this,true);YAHOO.widget.Logger._createNewCategory=function(A){this.categories.push(A);this.categoryCreateEvent.fire(A);};YAHOO.widget.Logger._isNewCategory=function(B){for(var A=0;A<this.categories.length;A++){if(B==this.categories[A]){return false;}}return true;};YAHOO.widget.Logger._createNewSource=function(A){this.sources.push(A);this.sourceCreateEvent.fire(A);};YAHOO.widget.Logger._isNewSource=function(A){if(A){for(var B=0;B<this.sources.length;B++){if(A==this.sources[B]){return false;}}return true;}};YAHOO.widget.Logger._printToBrowserConsole=function(C){if(window.console&&console.log){var E=C.category;var D=C.category.substring(0,4).toUpperCase();var G=C.time;var F;if(G.toLocaleTimeString){F=G.toLocaleTimeString();}else{F=G.toString();}var H=G.getTime();var B=(YAHOO.widget.Logger._lastTime)?(H-YAHOO.widget.Logger._lastTime):0;YAHOO.widget.Logger._lastTime=H;var A=F+\" (\"+B+\"ms): \"+C.source+\": \";if(YAHOO.env.ua.webkit){A+=C.msg;}console.log(A,C.msg);}};YAHOO.widget.Logger._onWindowError=function(A,C,B){try{YAHOO.widget.Logger.log(A+\" (\"+C+\", line \"+B+\")\",\"window\");if(YAHOO.widget.Logger._origOnWindowError){YAHOO.widget.Logger._origOnWindowError();}}catch(D){return false;}};YAHOO.widget.Logger.log(\"Logger initialized\");}(function(){var C=YAHOO.widget.Logger,D=YAHOO.util,E=D.Dom,A=D.Event,G=document;function B(I,H){I=G.createElement(I);if(H){for(var J in H){if(H.hasOwnProperty(J)){I[J]=H[J];}}}return I;}function F(I,H){this._sName=F._index;F._index++;this._init.apply(this,arguments);if(this.autoRender!==false){this.render();}}YAHOO.lang.augmentObject(F,{_index:0,ENTRY_TEMPLATE:(function(){return B(\"pre\",{className:\"yui-log-entry\"});})(),VERBOSE_TEMPLATE:\"<p><span class='{category}'>{label}</span> {totalTime}ms (+{elapsedTime}) {localTime}:</p><p>{sourceAndDetail}</p><p>{message}</p>\",BASIC_TEMPLATE:\"<p><span class='{category}'>{label}</span> {totalTime}ms (+{elapsedTime}) {localTime}: {sourceAndDetail}: {message}</p>\"});F.prototype={logReaderEnabled:true,width:null,height:null,top:null,left:null,right:null,bottom:null,fontSize:null,footerEnabled:true,verboseOutput:true,entryFormat:null,newestOnTop:true,outputBuffer:100,thresholdMax:500,thresholdMin:100,isCollapsed:false,isPaused:false,draggable:true,toString:function(){return\"LogReader instance\"+this._sName;},pause:function(){this.isPaused=true;this._timeout=null;this.logReaderEnabled=false;if(this._btnPause){this._btnPause.value=\"Resume\";\n}},resume:function(){this.isPaused=false;this.logReaderEnabled=true;this._printBuffer();if(this._btnPause){this._btnPause.value=\"Pause\";}},render:function(){if(this.rendered){return;}this._initContainerEl();this._initHeaderEl();this._initConsoleEl();this._initFooterEl();this._initCategories();this._initSources();this._initDragDrop();C.newLogEvent.subscribe(this._onNewLog,this);C.logResetEvent.subscribe(this._onReset,this);C.categoryCreateEvent.subscribe(this._onCategoryCreate,this);C.sourceCreateEvent.subscribe(this._onSourceCreate,this);this.rendered=true;this._filterLogs();},destroy:function(){A.purgeElement(this._elContainer,true);this._elContainer.innerHTML=\"\";this._elContainer.parentNode.removeChild(this._elContainer);this.rendered=false;},hide:function(){this._elContainer.style.display=\"none\";},show:function(){this._elContainer.style.display=\"block\";},collapse:function(){this._elConsole.style.display=\"none\";if(this._elFt){this._elFt.style.display=\"none\";}this._btnCollapse.value=\"Expand\";this.isCollapsed=true;},expand:function(){this._elConsole.style.display=\"block\";if(this._elFt){this._elFt.style.display=\"block\";}this._btnCollapse.value=\"Collapse\";this.isCollapsed=false;},getCheckbox:function(H){return this._filterCheckboxes[H];},getCategories:function(){return this._categoryFilters;},showCategory:function(I){var K=this._categoryFilters;if(K.indexOf){if(K.indexOf(I)>-1){return;}}else{for(var H=0;H<K.length;H++){if(K[H]===I){return;}}}this._categoryFilters.push(I);this._filterLogs();var J=this.getCheckbox(I);if(J){J.checked=true;}},hideCategory:function(I){var K=this._categoryFilters;for(var H=0;H<K.length;H++){if(I==K[H]){K.splice(H,1);break;}}this._filterLogs();var J=this.getCheckbox(I);if(J){J.checked=false;}},getSources:function(){return this._sourceFilters;},showSource:function(H){var K=this._sourceFilters;if(K.indexOf){if(K.indexOf(H)>-1){return;}}else{for(var I=0;I<K.length;I++){if(H==K[I]){return;}}}K.push(H);this._filterLogs();var J=this.getCheckbox(H);if(J){J.checked=true;}},hideSource:function(H){var K=this._sourceFilters;for(var I=0;I<K.length;I++){if(H==K[I]){K.splice(I,1);break;}}this._filterLogs();var J=this.getCheckbox(H);if(J){J.checked=false;}},clearConsole:function(){this._timeout=null;this._buffer=[];this._consoleMsgCount=0;var H=this._elConsole;H.innerHTML=\"\";},setTitle:function(H){this._title.innerHTML=this.html2Text(H);},getLastTime:function(){return this._lastTime;},formatMsg:function(I){var H=this.entryFormat||(this.verboseOutput?F.VERBOSE_TEMPLATE:F.BASIC_TEMPLATE),J={category:I.category,label:I.category.substring(0,4).toUpperCase(),sourceAndDetail:I.sourceDetail?I.source+\" \"+I.sourceDetail:I.source,message:this.html2Text(I.msg||I.message||\"\")};if(I.time&&I.time.getTime){J.localTime=I.time.toLocaleTimeString?I.time.toLocaleTimeString():I.time.toString();J.elapsedTime=I.time.getTime()-this.getLastTime();J.totalTime=I.time.getTime()-C.getStartTime();}var K=F.ENTRY_TEMPLATE.cloneNode(true);if(this.verboseOutput){K.className+=\" yui-log-verbose\";}K.innerHTML=H.replace(/\\{(\\w+)\\}/g,function(L,M){return(M in J)?J[M]:\"\";});return K;},html2Text:function(H){if(H){H+=\"\";return H.replace(/&/g,\"&#38;\").replace(/</g,\"&#60;\").replace(/>/g,\"&#62;\");}return\"\";},_sName:null,_buffer:null,_consoleMsgCount:0,_lastTime:null,_timeout:null,_filterCheckboxes:null,_categoryFilters:null,_sourceFilters:null,_elContainer:null,_elHd:null,_elCollapse:null,_btnCollapse:null,_title:null,_elConsole:null,_elFt:null,_elBtns:null,_elCategoryFilters:null,_elSourceFilters:null,_btnPause:null,_btnClear:null,_init:function(H,I){this._buffer=[];this._filterCheckboxes={};this._lastTime=C.getStartTime();if(I&&(I.constructor==Object)){for(var J in I){if(I.hasOwnProperty(J)){this[J]=I[J];}}}this._elContainer=E.get(H);YAHOO.log(\"LogReader initialized\",null,this.toString());},_initContainerEl:function(){if(!this._elContainer||!/div$/i.test(this._elContainer.tagName)){this._elContainer=G.body.insertBefore(B(\"div\"),G.body.firstChild);E.addClass(this._elContainer,\"yui-log-container\");}E.addClass(this._elContainer,\"yui-log\");var J=this._elContainer.style,H=[\"width\",\"right\",\"top\",\"fontSize\"],K,I;for(I=H.length-1;I>=0;--I){K=H[I];if(this[K]){J[K]=this[K];}}if(this.left){J.left=this.left;J.right=\"auto\";}if(this.bottom){J.bottom=this.bottom;J.top=\"auto\";}if(YAHOO.env.ua.opera){G.body.style+=\"\";}},_initHeaderEl:function(){if(this._elHd){A.purgeElement(this._elHd,true);this._elHd.innerHTML=\"\";}this._elHd=B(\"div\",{id:\"yui-log-hd\"+this._sName,className:\"yui-log-hd\"});this._elCollapse=B(\"div\",{className:\"yui-log-btns\"});this._btnCollapse=B(\"input\",{type:\"button\",className:\"yui-log-button\",value:\"Collapse\"});A.on(this._btnCollapse,\"click\",this._onClickCollapseBtn,this);this._title=B(\"h4\",{innerHTML:\"Logger Console\"});this._elCollapse.appendChild(this._btnCollapse);this._elHd.appendChild(this._elCollapse);this._elHd.appendChild(this._title);this._elContainer.appendChild(this._elHd);},_initConsoleEl:function(){if(this._elConsole){A.purgeElement(this._elConsole,true);this._elConsole.innerHTML=\"\";}this._elConsole=B(\"div\",{className:\"yui-log-bd\"});if(this.height){this._elConsole.style.height=this.height;}this._elContainer.appendChild(this._elConsole);},_initFooterEl:function(){if(this.footerEnabled){if(this._elFt){A.purgeElement(this._elFt,true);this._elFt.innerHTML=\"\";}this._elFt=B(\"div\",{className:\"yui-log-ft\"});this._elBtns=B(\"div\",{className:\"yui-log-btns\"});this._btnPause=B(\"input\",{type:\"button\",className:\"yui-log-button\",value:\"Pause\"});A.on(this._btnPause,\"click\",this._onClickPauseBtn,this);this._btnClear=B(\"input\",{type:\"button\",className:\"yui-log-button\",value:\"Clear\"});A.on(this._btnClear,\"click\",this._onClickClearBtn,this);this._elCategoryFilters=B(\"div\",{className:\"yui-log-categoryfilters\"});this._elSourceFilters=B(\"div\",{className:\"yui-log-sourcefilters\"});this._elBtns.appendChild(this._btnPause);this._elBtns.appendChild(this._btnClear);this._elFt.appendChild(this._elBtns);this._elFt.appendChild(this._elCategoryFilters);\nthis._elFt.appendChild(this._elSourceFilters);this._elContainer.appendChild(this._elFt);}},_initDragDrop:function(){if(D.DD&&this.draggable&&this._elHd){var H=new D.DD(this._elContainer);H.setHandleElId(this._elHd.id);this._elHd.style.cursor=\"move\";}},_initCategories:function(){this._categoryFilters=[];var J=C.categories;for(var H=0;H<J.length;H++){var I=J[H];this._categoryFilters.push(I);if(this._elCategoryFilters){this._createCategoryCheckbox(I);}}},_initSources:function(){this._sourceFilters=[];var J=C.sources;for(var I=0;I<J.length;I++){var H=J[I];this._sourceFilters.push(H);if(this._elSourceFilters){this._createSourceCheckbox(H);}}},_createCategoryCheckbox:function(K){if(this._elFt){var J=B(\"span\",{className:\"yui-log-filtergrp\"}),H=B(\"input\",{id:\"yui-log-filter-\"+K+this._sName,className:\"yui-log-filter-\"+K,type:\"checkbox\",category:K}),I=B(\"label\",{htmlFor:H.id,className:K,innerHTML:K});A.on(H,\"click\",this._onCheckCategory,this);this._filterCheckboxes[K]=H;J.appendChild(H);J.appendChild(I);this._elCategoryFilters.appendChild(J);H.checked=true;}},_createSourceCheckbox:function(H){if(this._elFt){var K=B(\"span\",{className:\"yui-log-filtergrp\"}),I=B(\"input\",{id:\"yui-log-filter-\"+H+this._sName,className:\"yui-log-filter-\"+H,type:\"checkbox\",source:H}),J=B(\"label\",{htmlFor:I.id,className:H,innerHTML:H});A.on(I,\"click\",this._onCheckSource,this);this._filterCheckboxes[H]=I;K.appendChild(I);K.appendChild(J);this._elSourceFilters.appendChild(K);I.checked=true;}},_filterLogs:function(){if(this._elConsole!==null){this.clearConsole();this._printToConsole(C.getStack());}},_printBuffer:function(){this._timeout=null;if(this._elConsole!==null){var I=this.thresholdMax;I=(I&&!isNaN(I))?I:500;if(this._consoleMsgCount<I){var H=[];for(var J=0;J<this._buffer.length;J++){H[J]=this._buffer[J];}this._buffer=[];this._printToConsole(H);}else{this._filterLogs();}if(!this.newestOnTop){this._elConsole.scrollTop=this._elConsole.scrollHeight;}}},_printToConsole:function(P){var I=P.length,T=G.createDocumentFragment(),W=[],X=this.thresholdMin,J=this._sourceFilters.length,U=this._categoryFilters.length,R,O,N,M,S;if(isNaN(X)||(X>this.thresholdMax)){X=0;}R=(I>X)?(I-X):0;for(O=R;O<I;O++){var L=false,Q=false,V=P[O],H=V.source,K=V.category;for(N=0;N<J;N++){if(H==this._sourceFilters[N]){Q=true;break;}}if(Q){for(N=0;N<U;N++){if(K==this._categoryFilters[N]){L=true;break;}}}if(L){if(this._consoleMsgCount===0){this._lastTime=V.time.getTime();}M=this.formatMsg(V);if(typeof M===\"string\"){W[W.length]=M;}else{T.insertBefore(M,this.newestOnTop?T.firstChild||null:null);}this._consoleMsgCount++;this._lastTime=V.time.getTime();}}if(W.length){W.splice(0,0,this._elConsole.innerHTML);this._elConsole.innerHTML=this.newestOnTop?W.reverse().join(\"\"):W.join(\"\");}else{if(T.firstChild){this._elConsole.insertBefore(T,this.newestOnTop?this._elConsole.firstChild||null:null);}}},_onCategoryCreate:function(K,J,H){var I=J[0];H._categoryFilters.push(I);if(H._elFt){H._createCategoryCheckbox(I);}},_onSourceCreate:function(K,J,H){var I=J[0];H._sourceFilters.push(I);if(H._elFt){H._createSourceCheckbox(I);}},_onCheckCategory:function(H,I){var J=this.category;if(!this.checked){I.hideCategory(J);}else{I.showCategory(J);}},_onCheckSource:function(H,I){var J=this.source;if(!this.checked){I.hideSource(J);}else{I.showSource(J);}},_onClickCollapseBtn:function(H,I){if(!I.isCollapsed){I.collapse();}else{I.expand();}},_onClickPauseBtn:function(H,I){if(!I.isPaused){I.pause();}else{I.resume();}},_onClickClearBtn:function(H,I){I.clearConsole();},_onNewLog:function(K,J,H){var I=J[0];H._buffer.push(I);if(H.logReaderEnabled===true&&H._timeout===null){H._timeout=setTimeout(function(){H._printBuffer();},H.outputBuffer);}},_onReset:function(J,I,H){H._filterLogs();}};YAHOO.widget.LogReader=F;})();YAHOO.register(\"logger\",YAHOO.widget.Logger,{version:\"2.8.1\",build:\"19\"});/*\nCopyright (c) 2010, Yahoo! Inc. All rights reserved.\nCode licensed under the BSD License:\nhttp://developer.yahoo.com/yui/license.html\nversion: 2.8.1\n*/\nYAHOO.namespace(\"tool\");(function(){var A=0;YAHOO.tool.TestCase=function(B){this._should={};for(var C in B){this[C]=B[C];}if(!YAHOO.lang.isString(this.name)){this.name=\"testCase\"+(A++);}};YAHOO.tool.TestCase.prototype={resume:function(B){YAHOO.tool.TestRunner.resume(B);},wait:function(D,C){var B=arguments;if(YAHOO.lang.isFunction(B[0])){throw new YAHOO.tool.TestCase.Wait(B[0],B[1]);}else{throw new YAHOO.tool.TestCase.Wait(function(){YAHOO.util.Assert.fail(\"Timeout: wait() called but resume() never called.\");},(YAHOO.lang.isNumber(B[0])?B[0]:10000));}},setUp:function(){},tearDown:function(){}};YAHOO.tool.TestCase.Wait=function(C,B){this.segment=(YAHOO.lang.isFunction(C)?C:null);this.delay=(YAHOO.lang.isNumber(B)?B:0);};})();YAHOO.namespace(\"tool\");YAHOO.tool.TestSuite=function(A){this.name=\"\";this.items=[];if(YAHOO.lang.isString(A)){this.name=A;}else{if(YAHOO.lang.isObject(A)){YAHOO.lang.augmentObject(this,A,true);}}if(this.name===\"\"){this.name=YAHOO.util.Dom.generateId(null,\"testSuite\");}};YAHOO.tool.TestSuite.prototype={add:function(A){if(A instanceof YAHOO.tool.TestSuite||A instanceof YAHOO.tool.TestCase){this.items.push(A);}},setUp:function(){},tearDown:function(){}};YAHOO.namespace(\"tool\");YAHOO.tool.TestRunner=(function(){function B(C){this.testObject=C;this.firstChild=null;this.lastChild=null;this.parent=null;this.next=null;this.results={passed:0,failed:0,total:0,ignored:0};if(C instanceof YAHOO.tool.TestSuite){this.results.type=\"testsuite\";this.results.name=C.name;}else{if(C instanceof YAHOO.tool.TestCase){this.results.type=\"testcase\";this.results.name=C.name;}}}B.prototype={appendChild:function(C){var D=new B(C);if(this.firstChild===null){this.firstChild=this.lastChild=D;}else{this.lastChild.next=D;this.lastChild=D;}D.parent=this;return D;}};function A(){A.superclass.constructor.apply(this,arguments);this.masterSuite=new YAHOO.tool.TestSuite(\"YUI Test Results\");this._cur=null;this._root=null;var D=[this.TEST_CASE_BEGIN_EVENT,this.TEST_CASE_COMPLETE_EVENT,this.TEST_SUITE_BEGIN_EVENT,this.TEST_SUITE_COMPLETE_EVENT,this.TEST_PASS_EVENT,this.TEST_FAIL_EVENT,this.TEST_IGNORE_EVENT,this.COMPLETE_EVENT,this.BEGIN_EVENT];for(var C=0;C<D.length;C++){this.createEvent(D[C],{scope:this});}}YAHOO.lang.extend(A,YAHOO.util.EventProvider,{TEST_CASE_BEGIN_EVENT:\"testcasebegin\",TEST_CASE_COMPLETE_EVENT:\"testcasecomplete\",TEST_SUITE_BEGIN_EVENT:\"testsuitebegin\",TEST_SUITE_COMPLETE_EVENT:\"testsuitecomplete\",TEST_PASS_EVENT:\"pass\",TEST_FAIL_EVENT:\"fail\",TEST_IGNORE_EVENT:\"ignore\",COMPLETE_EVENT:\"complete\",BEGIN_EVENT:\"begin\",_addTestCaseToTestTree:function(C,D){var E=C.appendChild(D);for(var F in D){if(F.indexOf(\"test\")===0&&YAHOO.lang.isFunction(D[F])){E.appendChild(F);}}},_addTestSuiteToTestTree:function(C,F){var E=C.appendChild(F);for(var D=0;D<F.items.length;D++){if(F.items[D] instanceof YAHOO.tool.TestSuite){this._addTestSuiteToTestTree(E,F.items[D]);}else{if(F.items[D] instanceof YAHOO.tool.TestCase){this._addTestCaseToTestTree(E,F.items[D]);}}}},_buildTestTree:function(){this._root=new B(this.masterSuite);this._cur=this._root;for(var C=0;C<this.masterSuite.items.length;C++){if(this.masterSuite.items[C] instanceof YAHOO.tool.TestSuite){this._addTestSuiteToTestTree(this._root,this.masterSuite.items[C]);}else{if(this.masterSuite.items[C] instanceof YAHOO.tool.TestCase){this._addTestCaseToTestTree(this._root,this.masterSuite.items[C]);}}}},_handleTestObjectComplete:function(C){if(YAHOO.lang.isObject(C.testObject)){C.parent.results.passed+=C.results.passed;C.parent.results.failed+=C.results.failed;C.parent.results.total+=C.results.total;C.parent.results.ignored+=C.results.ignored;C.parent.results[C.testObject.name]=C.results;if(C.testObject instanceof YAHOO.tool.TestSuite){C.testObject.tearDown();this.fireEvent(this.TEST_SUITE_COMPLETE_EVENT,{testSuite:C.testObject,results:C.results});}else{if(C.testObject instanceof YAHOO.tool.TestCase){this.fireEvent(this.TEST_CASE_COMPLETE_EVENT,{testCase:C.testObject,results:C.results});}}}},_next:function(){if(this._cur.firstChild){this._cur=this._cur.firstChild;}else{if(this._cur.next){this._cur=this._cur.next;}else{while(this._cur&&!this._cur.next&&this._cur!==this._root){this._handleTestObjectComplete(this._cur);this._cur=this._cur.parent;}if(this._cur==this._root){this._cur.results.type=\"report\";this._cur.results.timestamp=(new Date()).toLocaleString();this._cur.results.duration=(new Date())-this._cur.results.duration;this.fireEvent(this.COMPLETE_EVENT,{results:this._cur.results});this._cur=null;}else{this._handleTestObjectComplete(this._cur);this._cur=this._cur.next;}}}return this._cur;},_run:function(){var E=false;var D=this._next();if(D!==null){var C=D.testObject;if(YAHOO.lang.isObject(C)){if(C instanceof YAHOO.tool.TestSuite){this.fireEvent(this.TEST_SUITE_BEGIN_EVENT,{testSuite:C});C.setUp();}else{if(C instanceof YAHOO.tool.TestCase){this.fireEvent(this.TEST_CASE_BEGIN_EVENT,{testCase:C});}}if(typeof setTimeout!=\"undefined\"){setTimeout(function(){YAHOO.tool.TestRunner._run();},0);}else{this._run();}}else{this._runTest(D);}}},_resumeTest:function(G){var C=this._cur;var H=C.testObject;var E=C.parent.testObject;if(E.__yui_wait){clearTimeout(E.__yui_wait);delete E.__yui_wait;}var K=(E._should.fail||{})[H];var D=(E._should.error||{})[H];var F=false;var I=null;try{G.apply(E);if(K){I=new YAHOO.util.ShouldFail();F=true;}else{if(D){I=new YAHOO.util.ShouldError();F=true;}}}catch(J){if(J instanceof YAHOO.util.AssertionError){if(!K){I=J;F=true;}}else{if(J instanceof YAHOO.tool.TestCase.Wait){if(YAHOO.lang.isFunction(J.segment)){if(YAHOO.lang.isNumber(J.delay)){if(typeof setTimeout!=\"undefined\"){E.__yui_wait=setTimeout(function(){YAHOO.tool.TestRunner._resumeTest(J.segment);},J.delay);}else{throw new Error(\"Asynchronous tests not supported in this environment.\");}}}return;}else{if(!D){I=new YAHOO.util.UnexpectedError(J);F=true;}else{if(YAHOO.lang.isString(D)){if(J.message!=D){I=new YAHOO.util.UnexpectedError(J);F=true;}}else{if(YAHOO.lang.isFunction(D)){if(!(J instanceof D)){I=new YAHOO.util.UnexpectedError(J);\nF=true;}}else{if(YAHOO.lang.isObject(D)){if(!(J instanceof D.constructor)||J.message!=D.message){I=new YAHOO.util.UnexpectedError(J);F=true;}}}}}}}}if(F){this.fireEvent(this.TEST_FAIL_EVENT,{testCase:E,testName:H,error:I});}else{this.fireEvent(this.TEST_PASS_EVENT,{testCase:E,testName:H});}E.tearDown();C.parent.results[H]={result:F?\"fail\":\"pass\",message:I?I.getMessage():\"Test passed\",type:\"test\",name:H};if(F){C.parent.results.failed++;}else{C.parent.results.passed++;}C.parent.results.total++;if(typeof setTimeout!=\"undefined\"){setTimeout(function(){YAHOO.tool.TestRunner._run();},0);}else{this._run();}},_runTest:function(F){var C=F.testObject;var D=F.parent.testObject;var G=D[C];var E=(D._should.ignore||{})[C];if(E){F.parent.results[C]={result:\"ignore\",message:\"Test ignored\",type:\"test\",name:C};F.parent.results.ignored++;F.parent.results.total++;this.fireEvent(this.TEST_IGNORE_EVENT,{testCase:D,testName:C});if(typeof setTimeout!=\"undefined\"){setTimeout(function(){YAHOO.tool.TestRunner._run();},0);}else{this._run();}}else{D.setUp();this._resumeTest(G);}},fireEvent:function(C,D){D=D||{};D.type=C;A.superclass.fireEvent.call(this,C,D);},add:function(C){this.masterSuite.add(C);},clear:function(){this.masterSuite.items=[];},resume:function(C){this._resumeTest(C||function(){});},run:function(C){var D=YAHOO.tool.TestRunner;D._buildTestTree();D._root.results.duration=(new Date()).getTime();D.fireEvent(D.BEGIN_EVENT);D._run();}});return new A();})();YAHOO.namespace(\"util\");YAHOO.util.Assert={_formatMessage:function(B,A){var C=B;if(YAHOO.lang.isString(B)&&B.length>0){return YAHOO.lang.substitute(B,{message:A});}else{return A;}},fail:function(A){throw new YAHOO.util.AssertionError(this._formatMessage(A,\"Test force-failed.\"));},areEqual:function(B,C,A){if(B!=C){throw new YAHOO.util.ComparisonFailure(this._formatMessage(A,\"Values should be equal.\"),B,C);}},areNotEqual:function(A,C,B){if(A==C){throw new YAHOO.util.UnexpectedValue(this._formatMessage(B,\"Values should not be equal.\"),A);}},areNotSame:function(A,C,B){if(A===C){throw new YAHOO.util.UnexpectedValue(this._formatMessage(B,\"Values should not be the same.\"),A);}},areSame:function(B,C,A){if(B!==C){throw new YAHOO.util.ComparisonFailure(this._formatMessage(A,\"Values should be the same.\"),B,C);}},isFalse:function(B,A){if(false!==B){throw new YAHOO.util.ComparisonFailure(this._formatMessage(A,\"Value should be false.\"),false,B);}},isTrue:function(B,A){if(true!==B){throw new YAHOO.util.ComparisonFailure(this._formatMessage(A,\"Value should be true.\"),true,B);}},isNaN:function(B,A){if(!isNaN(B)){throw new YAHOO.util.ComparisonFailure(this._formatMessage(A,\"Value should be NaN.\"),NaN,B);}},isNotNaN:function(B,A){if(isNaN(B)){throw new YAHOO.util.UnexpectedValue(this._formatMessage(A,\"Values should not be NaN.\"),NaN);}},isNotNull:function(B,A){if(YAHOO.lang.isNull(B)){throw new YAHOO.util.UnexpectedValue(this._formatMessage(A,\"Values should not be null.\"),null);}},isNotUndefined:function(B,A){if(YAHOO.lang.isUndefined(B)){throw new YAHOO.util.UnexpectedValue(this._formatMessage(A,\"Value should not be undefined.\"),undefined);}},isNull:function(B,A){if(!YAHOO.lang.isNull(B)){throw new YAHOO.util.ComparisonFailure(this._formatMessage(A,\"Value should be null.\"),null,B);}},isUndefined:function(B,A){if(!YAHOO.lang.isUndefined(B)){throw new YAHOO.util.ComparisonFailure(this._formatMessage(A,\"Value should be undefined.\"),undefined,B);}},isArray:function(B,A){if(!YAHOO.lang.isArray(B)){throw new YAHOO.util.UnexpectedValue(this._formatMessage(A,\"Value should be an array.\"),B);}},isBoolean:function(B,A){if(!YAHOO.lang.isBoolean(B)){throw new YAHOO.util.UnexpectedValue(this._formatMessage(A,\"Value should be a Boolean.\"),B);}},isFunction:function(B,A){if(!YAHOO.lang.isFunction(B)){throw new YAHOO.util.UnexpectedValue(this._formatMessage(A,\"Value should be a function.\"),B);}},isInstanceOf:function(B,C,A){if(!(C instanceof B)){throw new YAHOO.util.ComparisonFailure(this._formatMessage(A,\"Value isn't an instance of expected type.\"),B,C);}},isNumber:function(B,A){if(!YAHOO.lang.isNumber(B)){throw new YAHOO.util.UnexpectedValue(this._formatMessage(A,\"Value should be a number.\"),B);}},isObject:function(B,A){if(!YAHOO.lang.isObject(B)){throw new YAHOO.util.UnexpectedValue(this._formatMessage(A,\"Value should be an object.\"),B);}},isString:function(B,A){if(!YAHOO.lang.isString(B)){throw new YAHOO.util.UnexpectedValue(this._formatMessage(A,\"Value should be a string.\"),B);}},isTypeOf:function(B,C,A){if(typeof C!=B){throw new YAHOO.util.ComparisonFailure(this._formatMessage(A,\"Value should be of type \"+B+\".\"),B,typeof C);}}};YAHOO.util.AssertionError=function(A){this.message=A;this.name=\"AssertionError\";};YAHOO.lang.extend(YAHOO.util.AssertionError,Object,{getMessage:function(){return this.message;},toString:function(){return this.name+\": \"+this.getMessage();}});YAHOO.util.ComparisonFailure=function(B,A,C){YAHOO.util.AssertionError.call(this,B);this.expected=A;this.actual=C;this.name=\"ComparisonFailure\";};YAHOO.lang.extend(YAHOO.util.ComparisonFailure,YAHOO.util.AssertionError,{getMessage:function(){return this.message+\"\\nExpected: \"+this.expected+\" (\"+(typeof this.expected)+\")\"+\"\\nActual:\"+this.actual+\" (\"+(typeof this.actual)+\")\";}});YAHOO.util.UnexpectedValue=function(B,A){YAHOO.util.AssertionError.call(this,B);this.unexpected=A;this.name=\"UnexpectedValue\";};YAHOO.lang.extend(YAHOO.util.UnexpectedValue,YAHOO.util.AssertionError,{getMessage:function(){return this.message+\"\\nUnexpected: \"+this.unexpected+\" (\"+(typeof this.unexpected)+\") \";}});YAHOO.util.ShouldFail=function(A){YAHOO.util.AssertionError.call(this,A||\"This test should fail but didn't.\");this.name=\"ShouldFail\";};YAHOO.lang.extend(YAHOO.util.ShouldFail,YAHOO.util.AssertionError);YAHOO.util.ShouldError=function(A){YAHOO.util.AssertionError.call(this,A||\"This test should have thrown an error but didn't.\");this.name=\"ShouldError\";};YAHOO.lang.extend(YAHOO.util.ShouldError,YAHOO.util.AssertionError);YAHOO.util.UnexpectedError=function(A){YAHOO.util.AssertionError.call(this,\"Unexpected error: \"+A.message);\nthis.cause=A;this.name=\"UnexpectedError\";this.stack=A.stack;};YAHOO.lang.extend(YAHOO.util.UnexpectedError,YAHOO.util.AssertionError);YAHOO.util.ArrayAssert={contains:function(E,D,B){var C=false;var F=YAHOO.util.Assert;for(var A=0;A<D.length&&!C;A++){if(D[A]===E){C=true;}}if(!C){F.fail(F._formatMessage(B,\"Value \"+E+\" (\"+(typeof E)+\") not found in array [\"+D+\"].\"));}},containsItems:function(C,D,B){for(var A=0;A<C.length;A++){this.contains(C[A],D,B);}},containsMatch:function(E,D,B){if(typeof E!=\"function\"){throw new TypeError(\"ArrayAssert.containsMatch(): First argument must be a function.\");}var C=false;var F=YAHOO.util.Assert;for(var A=0;A<D.length&&!C;A++){if(E(D[A])){C=true;}}if(!C){F.fail(F._formatMessage(B,\"No match found in array [\"+D+\"].\"));}},doesNotContain:function(E,D,B){var C=false;var F=YAHOO.util.Assert;for(var A=0;A<D.length&&!C;A++){if(D[A]===E){C=true;}}if(C){F.fail(F._formatMessage(B,\"Value found in array [\"+D+\"].\"));}},doesNotContainItems:function(C,D,B){for(var A=0;A<C.length;A++){this.doesNotContain(C[A],D,B);}},doesNotContainMatch:function(E,D,B){if(typeof E!=\"function\"){throw new TypeError(\"ArrayAssert.doesNotContainMatch(): First argument must be a function.\");}var C=false;var F=YAHOO.util.Assert;for(var A=0;A<D.length&&!C;A++){if(E(D[A])){C=true;}}if(C){F.fail(F._formatMessage(B,\"Value found in array [\"+D+\"].\"));}},indexOf:function(E,D,A,C){for(var B=0;B<D.length;B++){if(D[B]===E){YAHOO.util.Assert.areEqual(A,B,C||\"Value exists at index \"+B+\" but should be at index \"+A+\".\");return;}}var F=YAHOO.util.Assert;F.fail(F._formatMessage(C,\"Value doesn't exist in array [\"+D+\"].\"));},itemsAreEqual:function(D,F,C){var A=Math.max(D.length,F.length||0);var E=YAHOO.util.Assert;for(var B=0;B<A;B++){E.areEqual(D[B],F[B],E._formatMessage(C,\"Values in position \"+B+\" are not equal.\"));}},itemsAreEquivalent:function(E,F,B,D){if(typeof B!=\"function\"){throw new TypeError(\"ArrayAssert.itemsAreEquivalent(): Third argument must be a function.\");}var A=Math.max(E.length,F.length||0);for(var C=0;C<A;C++){if(!B(E[C],F[C])){throw new YAHOO.util.ComparisonFailure(YAHOO.util.Assert._formatMessage(D,\"Values in position \"+C+\" are not equivalent.\"),E[C],F[C]);}}},isEmpty:function(C,A){if(C.length>0){var B=YAHOO.util.Assert;B.fail(B._formatMessage(A,\"Array should be empty.\"));}},isNotEmpty:function(C,A){if(C.length===0){var B=YAHOO.util.Assert;B.fail(B._formatMessage(A,\"Array should not be empty.\"));}},itemsAreSame:function(D,F,C){var A=Math.max(D.length,F.length||0);var E=YAHOO.util.Assert;for(var B=0;B<A;B++){E.areSame(D[B],F[B],E._formatMessage(C,\"Values in position \"+B+\" are not the same.\"));}},lastIndexOf:function(E,D,A,C){var F=YAHOO.util.Assert;for(var B=D.length;B>=0;B--){if(D[B]===E){F.areEqual(A,B,F._formatMessage(C,\"Value exists at index \"+B+\" but should be at index \"+A+\".\"));return;}}F.fail(F._formatMessage(C,\"Value doesn't exist in array.\"));}};YAHOO.namespace(\"util\");YAHOO.util.ObjectAssert={propertiesAreEqual:function(D,G,C){var F=YAHOO.util.Assert;var B=[];for(var E in D){B.push(E);}for(var A=0;A<B.length;A++){F.isNotUndefined(G[B[A]],F._formatMessage(C,\"Property '\"+B[A]+\"' expected.\"));}},hasProperty:function(A,B,C){if(!(A in B)){var D=YAHOO.util.Assert;D.fail(D._formatMessage(C,\"Property '\"+A+\"' not found on object.\"));}},hasOwnProperty:function(A,B,C){if(!YAHOO.lang.hasOwnProperty(B,A)){var D=YAHOO.util.Assert;D.fail(D._formatMessage(C,\"Property '\"+A+\"' not found on object instance.\"));}}};YAHOO.util.DateAssert={datesAreEqual:function(B,D,A){if(B instanceof Date&&D instanceof Date){var C=YAHOO.util.Assert;C.areEqual(B.getFullYear(),D.getFullYear(),C._formatMessage(A,\"Years should be equal.\"));C.areEqual(B.getMonth(),D.getMonth(),C._formatMessage(A,\"Months should be equal.\"));C.areEqual(B.getDate(),D.getDate(),C._formatMessage(A,\"Day of month should be equal.\"));}else{throw new TypeError(\"DateAssert.datesAreEqual(): Expected and actual values must be Date objects.\");}},timesAreEqual:function(B,D,A){if(B instanceof Date&&D instanceof Date){var C=YAHOO.util.Assert;C.areEqual(B.getHours(),D.getHours(),C._formatMessage(A,\"Hours should be equal.\"));C.areEqual(B.getMinutes(),D.getMinutes(),C._formatMessage(A,\"Minutes should be equal.\"));C.areEqual(B.getSeconds(),D.getSeconds(),C._formatMessage(A,\"Seconds should be equal.\"));}else{throw new TypeError(\"DateAssert.timesAreEqual(): Expected and actual values must be Date objects.\");}}};YAHOO.namespace(\"tool\");YAHOO.tool.TestManager={TEST_PAGE_BEGIN_EVENT:\"testpagebegin\",TEST_PAGE_COMPLETE_EVENT:\"testpagecomplete\",TEST_MANAGER_BEGIN_EVENT:\"testmanagerbegin\",TEST_MANAGER_COMPLETE_EVENT:\"testmanagercomplete\",_curPage:null,_frame:null,_logger:null,_timeoutId:0,_pages:[],_results:null,_handleTestRunnerComplete:function(A){this.fireEvent(this.TEST_PAGE_COMPLETE_EVENT,{page:this._curPage,results:A.results});this._processResults(this._curPage,A.results);this._logger.clearTestRunner();if(this._pages.length){this._timeoutId=setTimeout(function(){YAHOO.tool.TestManager._run();},1000);}else{this.fireEvent(this.TEST_MANAGER_COMPLETE_EVENT,this._results);}},_processResults:function(C,A){var B=this._results;B.passed+=A.passed;B.failed+=A.failed;B.ignored+=A.ignored;B.total+=A.total;B.duration+=A.duration;if(A.failed){B.failedPages.push(C);}else{B.passedPages.push(C);}A.name=C;A.type=\"page\";B[C]=A;},_run:function(){this._curPage=this._pages.shift();this.fireEvent(this.TEST_PAGE_BEGIN_EVENT,this._curPage);this._frame.location.replace(this._curPage);},load:function(){if(parent.YAHOO.tool.TestManager!==this){parent.YAHOO.tool.TestManager.load();}else{if(this._frame){var A=this._frame.YAHOO.tool.TestRunner;this._logger.setTestRunner(A);A.subscribe(A.COMPLETE_EVENT,this._handleTestRunnerComplete,this,true);A.run();}}},setPages:function(A){this._pages=A;},start:function(){if(!this._initialized){this.createEvent(this.TEST_PAGE_BEGIN_EVENT);this.createEvent(this.TEST_PAGE_COMPLETE_EVENT);this.createEvent(this.TEST_MANAGER_BEGIN_EVENT);this.createEvent(this.TEST_MANAGER_COMPLETE_EVENT);\nif(!this._frame){var A=document.createElement(\"iframe\");A.style.visibility=\"hidden\";A.style.position=\"absolute\";document.body.appendChild(A);this._frame=A.contentWindow||A.contentDocument.parentWindow;}if(!this._logger){this._logger=new YAHOO.tool.TestLogger();}this._initialized=true;}this._results={passed:0,failed:0,ignored:0,total:0,type:\"report\",name:\"YUI Test Results\",duration:0,failedPages:[],passedPages:[]};this.fireEvent(this.TEST_MANAGER_BEGIN_EVENT,null);this._run();},stop:function(){clearTimeout(this._timeoutId);}};YAHOO.lang.augmentObject(YAHOO.tool.TestManager,YAHOO.util.EventProvider.prototype);YAHOO.namespace(\"tool\");YAHOO.tool.TestLogger=function(B,A){YAHOO.tool.TestLogger.superclass.constructor.call(this,B,A);this.init();};YAHOO.lang.extend(YAHOO.tool.TestLogger,YAHOO.widget.LogReader,{footerEnabled:true,newestOnTop:false,formatMsg:function(B){var A=B.category;var C=this.html2Text(B.msg);return'<pre><p><span class=\"'+A+'\">'+A.toUpperCase()+\"</span> \"+C+\"</p></pre>\";},init:function(){if(YAHOO.tool.TestRunner){this.setTestRunner(YAHOO.tool.TestRunner);}this.hideSource(\"global\");this.hideSource(\"LogReader\");this.hideCategory(\"warn\");this.hideCategory(\"window\");this.hideCategory(\"time\");this.clearConsole();},clearTestRunner:function(){if(this._runner){this._runner.unsubscribeAll();this._runner=null;}},setTestRunner:function(A){if(this._runner){this.clearTestRunner();}this._runner=A;A.subscribe(A.TEST_PASS_EVENT,this._handleTestRunnerEvent,this,true);A.subscribe(A.TEST_FAIL_EVENT,this._handleTestRunnerEvent,this,true);A.subscribe(A.TEST_IGNORE_EVENT,this._handleTestRunnerEvent,this,true);A.subscribe(A.BEGIN_EVENT,this._handleTestRunnerEvent,this,true);A.subscribe(A.COMPLETE_EVENT,this._handleTestRunnerEvent,this,true);A.subscribe(A.TEST_SUITE_BEGIN_EVENT,this._handleTestRunnerEvent,this,true);A.subscribe(A.TEST_SUITE_COMPLETE_EVENT,this._handleTestRunnerEvent,this,true);A.subscribe(A.TEST_CASE_BEGIN_EVENT,this._handleTestRunnerEvent,this,true);A.subscribe(A.TEST_CASE_COMPLETE_EVENT,this._handleTestRunnerEvent,this,true);},_handleTestRunnerEvent:function(D){var A=YAHOO.tool.TestRunner;var C=\"\";var B=\"\";switch(D.type){case A.BEGIN_EVENT:C=\"Testing began at \"+(new Date()).toString()+\".\";B=\"info\";break;case A.COMPLETE_EVENT:C=\"Testing completed at \"+(new Date()).toString()+\".\\nPassed:\"+D.results.passed+\" Failed:\"+D.results.failed+\" Total:\"+D.results.total;B=\"info\";break;case A.TEST_FAIL_EVENT:C=D.testName+\": \"+D.error.getMessage();B=\"fail\";break;case A.TEST_IGNORE_EVENT:C=D.testName+\": ignored.\";B=\"ignore\";break;case A.TEST_PASS_EVENT:C=D.testName+\": passed.\";B=\"pass\";break;case A.TEST_SUITE_BEGIN_EVENT:C='Test suite \"'+D.testSuite.name+'\" started.';B=\"info\";break;case A.TEST_SUITE_COMPLETE_EVENT:C='Test suite \"'+D.testSuite.name+'\" completed.\\nPassed:'+D.results.passed+\" Failed:\"+D.results.failed+\" Total:\"+D.results.total;B=\"info\";break;case A.TEST_CASE_BEGIN_EVENT:C='Test case \"'+D.testCase.name+'\" started.';B=\"info\";break;case A.TEST_CASE_COMPLETE_EVENT:C='Test case \"'+D.testCase.name+'\" completed.\\nPassed:'+D.results.passed+\" Failed:\"+D.results.failed+\" Total:\"+D.results.total;B=\"info\";break;default:C=\"Unexpected event \"+D.type;C=\"info\";}YAHOO.log(C,B,\"TestRunner\");}});YAHOO.namespace(\"tool.TestFormat\");YAHOO.tool.TestFormat.JSON=function(A){return YAHOO.lang.JSON.stringify(A);};YAHOO.tool.TestFormat.XML=function(C){var A=YAHOO.lang;var B=\"<\"+C.type+' name=\"'+C.name.replace(/\"/g,\"&quot;\").replace(/'/g,\"&apos;\")+'\"';if(A.isNumber(C.duration)){B+=' duration=\"'+C.duration+'\"';}if(C.type==\"test\"){B+=' result=\"'+C.result+'\" message=\"'+C.message+'\">';}else{B+=' passed=\"'+C.passed+'\" failed=\"'+C.failed+'\" ignored=\"'+C.ignored+'\" total=\"'+C.total+'\">';for(var D in C){if(A.hasOwnProperty(C,D)&&A.isObject(C[D])&&!A.isArray(C[D])){B+=arguments.callee(C[D]);}}}B+=\"</\"+C.type+\">\";return B;};YAHOO.namespace(\"tool\");YAHOO.tool.TestReporter=function(A,B){this.url=A;this.format=B||YAHOO.tool.TestFormat.XML;this._fields=new Object();this._form=null;this._iframe=null;};YAHOO.tool.TestReporter.prototype={constructor:YAHOO.tool.TestReporter,_convertToISOString:function(A){function B(C){return C<10?\"0\"+C:C;}return A.getUTCFullYear()+\"-\"+B(A.getUTCMonth()+1)+\"-\"+B(A.getUTCDate())+\"T\"+B(A.getUTCHours())+\":\"+B(A.getUTCMinutes())+\":\"+B(A.getUTCSeconds())+\"Z\";},addField:function(A,B){this._fields[A]=B;},clearFields:function(){this._fields=new Object();},destroy:function(){if(this._form){this._form.parentNode.removeChild(this._form);this._form=null;}if(this._iframe){this._iframe.parentNode.removeChild(this._iframe);this._iframe=null;}this._fields=null;},report:function(A){if(!this._form){this._form=document.createElement(\"form\");this._form.method=\"post\";this._form.style.visibility=\"hidden\";this._form.style.position=\"absolute\";this._form.style.top=0;document.body.appendChild(this._form);if(YAHOO.env.ua.ie){this._iframe=document.createElement('<iframe name=\"yuiTestTarget\" />');}else{this._iframe=document.createElement(\"iframe\");this._iframe.name=\"yuiTestTarget\";}this._iframe.src=\"javascript:false\";this._iframe.style.visibility=\"hidden\";this._iframe.style.position=\"absolute\";this._iframe.style.top=0;document.body.appendChild(this._iframe);this._form.target=\"yuiTestTarget\";}this._form.action=this.url;while(this._form.hasChildNodes()){this._form.removeChild(this._form.lastChild);}this._fields.results=this.format(A);this._fields.useragent=navigator.userAgent;this._fields.timestamp=this._convertToISOString(new Date());for(var B in this._fields){if(YAHOO.lang.hasOwnProperty(this._fields,B)&&typeof this._fields[B]!=\"function\"){if(YAHOO.env.ua.ie){input=document.createElement('<input name=\"'+B+'\" >');}else{input=document.createElement(\"input\");input.name=B;}input.type=\"hidden\";input.value=this._fields[B];this._form.appendChild(input);}}delete this._fields.results;delete this._fields.useragent;delete this._fields.timestamp;if(arguments[1]!==false){this._form.submit();}}};YAHOO.register(\"yuitest\",YAHOO.tool.TestRunner,{version:\"2.8.1\",build:\"19\"});\n"
  },
  {
    "path": "dirigible/shared/static/jquery/jeip.js",
    "content": "/*\nCopyright (c) 2008 Joseph Scott, http://josephscott.org/\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n// version: 0.1.2\n\n(function( $ ) {\n\t$.fn.eip = function( save_url, options ) {\n\t\t// Defaults\n\t\tvar opt = {\n\t\t\tsave_url\t\t\t: save_url,\n\n\t\t\tsave_on_enter\t\t: true,\n\t\t\tcancel_on_esc\t\t: true,\n\t\t\tfocus_edit\t\t\t: true,\n\t\t\tselect_text\t\t\t: false,\n\t\t\tedit_event\t\t\t: \"click\",\n\t\t\tselect_options\t\t: false,\n\t\t\tdata\t\t\t\t: false,\n\n\t\t\tform_type\t\t\t: \"text\", // text, textarea, select\n\t\t\tsize\t\t\t\t: false, // calculate at run time\n\t\t\tmax_size\t\t\t: 60,\n\t\t\trows\t\t\t\t: false, // calculate at run time\n\t\t\tmax_rows\t\t\t: 10,\n\t\t\tcols\t\t\t\t: 60,\n\n\t\t\tsavebutton_text\t\t: \"SAVE\",\n\t\t\tsavebutton_class\t: \"jeip-savebutton\",\n\t\t\tcancelbutton_text\t: \"CANCEL\",\n\t\t\tcancelbutton_class\t: \"jeip-cancelbutton\",\n\n\t\t\tmouseover_class\t\t: \"jeip-mouseover\",\n\t\t\teditor_class\t\t: \"jeip-editor\",\n\t\t\teditfield_class\t\t: \"jeip-editfield\",\n\n\t\t\tsaving_text\t\t\t: \"Saving ...\",\n\t\t\tsaving_class\t\t: \"jeip-saving\",\n\n\t\t\tsaving\t\t\t\t: '<span id=\"saving-#{id}\" class=\"#{saving_class}\" style=\"display: none;\">#{saving_text}</span>',\n\n\t\t\tstart_form\t\t\t: '<span id=\"editor-#{id}\" class=\"#{editor_class}\" style=\"display: none;\">',\n\t\t\tform_buttons\t\t: '<span><input type=\"button\" id=\"save-#{id}\" class=\"#{savebutton_class}\" value=\"#{savebutton_text}\" /> OR <input type=\"button\" id=\"cancel-#{id}\" class=\"#{cancelbutton_class}\" value=\"#{cancelbutton_text}\" /></span>',\n\t\t\tstop_form\t\t\t: '</span>',\n\n\t\t\ttext_form\t\t\t: '<input type=\"text\" id=\"edit-#{id}\" class=\"#{editfield_class}\" value=\"#{value}\" /> <br />',\n\t\t\ttextarea_form\t\t: '<textarea cols=\"#{cols}\" rows=\"#{rows}\" id=\"edit-#{id}\" class=\"#{editfield_class}\">#{value}</textarea> <br />',\n\t\t\tstart_select_form\t: '<select id=\"edit-#{id}\" class=\"#{editfield_clas}\">',\n\t\t\tselect_option_form\t: '<option id=\"edit-option-#{id}-#{option_value}\" value=\"#{option_value}\" #{selected}>#{option_text}</option>',\n\t\t\tstop_select_form\t: '</select>',\n\n\t\t\tafter_save\t\t\t: function( self ) {\n\t\t\t\tfor( var i = 0; i < 2; i++ ) {\n\t\t\t\t\t$( self ).fadeOut( \"fast\" );\n\t\t\t\t\t$( self ).fadeIn( \"fast\" );\n\t\t\t\t}\n\t\t\t},\n\t\t\ton_error\t\t\t: function( msg ) {\n\t\t\t\talert( \"Error: \" + msg );\n\t\t\t}\n\t\t}; // defaults\n\n\t\tif( options ) {\n\t\t\t$.extend( opt, options );\n\t\t}\n\n\t\tthis.each( function( ) {\n\t\t\tvar self = this;\n\n\t\t\t$( this ).bind( \"mouseenter mouseleave\", function( e ) {\n\t\t\t\t$( this ).toggleClass( opt.mouseover_class );\n\t\t\t} );\n\n\t\t\t$( this ).bind( opt.edit_event, function( e ) {\n\t\t\t\t_editMode( this );\n\t\t\t} );\n\t\t} ); // this.each\n\n\t\t// Private functions\n\t\tvar _editMode = function( self ) {\n\t\t\t$( self ).unbind( opt.edit_event );\n\n\t\t\t$( self ).removeClass( opt.mouseover_class );\n\t\t\t$( self ).fadeOut( \"fast\", function( e ) {\n\t\t\t\tvar id\t\t= self.id;\n\t\t\t\tvar value\t= $( self ).html( );\n\n\t\t\t\tvar safe_value\t= value.replace( /</g, \"&lt;\" );\n\t\t\t\tsafe_value\t\t= value.replace( />/g, \"&gt;\" );\n\t\t\t\tsafe_value\t\t= value.replace( /\"/g, \"&qout;\" );\n\n\t\t\t\tvar orig_option_value = false;\n\n\t\t\t\tvar form = _template( opt.start_form, {\n\t\t\t\t\tid\t\t\t\t: self.id,\n\t\t\t\t\teditor_class\t: opt.editor_class\n\t\t\t\t} );\n\n\t\t\t\tif( opt.form_type == 'text' ) {\n\t\t\t\t\tform += _template( opt.text_form, {\n\t\t\t\t\t\tid\t\t\t\t: self.id,\n\t\t\t\t\t\teditfield_class\t: opt.editfield_class,\n\t\t\t\t\t\tvalue\t\t\t: value\n\t\t\t\t\t} );\n\t\t\t\t} // text form\n\t\t\t\telse if( opt.form_type == 'textarea' ) {\n\t\t\t\t\tvar length = value.length;\n\t\t\t\t\tvar rows = ( length / opt.cols ) + 2;\n\n\t\t\t\t\tfor( var i = 0; i < length; i++ ) {\n\t\t\t\t\t\tif( value.charAt( i ) == \"\\n\" ) {\n\t\t\t\t\t\t\trows++;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif( rows > opt.max_rows ) {\n\t\t\t\t\t\trows = opt.max_rows;\n\t\t\t\t\t}\n\t\t\t\t\tif( opt.rows != false ) {\n\t\t\t\t\t\trows = opt.rows;\n\t\t\t\t\t}\n\t\t\t\t\trows = parseInt( rows );\n\n\t\t\t\t\tform += _template( opt.textarea_form, {\n\t\t\t\t\t\tid\t\t\t\t: self.id,\n\t\t\t\t\t\tcols\t\t\t: opt.cols,\n\t\t\t\t\t\trows\t\t\t: rows,\n\t\t\t\t\t\teditfield_class\t: opt.editfield_class,\n\t\t\t\t\t\tvalue\t\t\t: value\n\t\t\t\t\t} );\n\t\t\t\t} // textarea form\n\t\t\t\telse if( opt.form_type == 'select' ) {\n\t\t\t\t\tform += _template( opt.start_select_form, {\n\t\t\t\t\t\tid\t\t\t\t: self.id,\n\t\t\t\t\t\teditfield_class\t: opt.editfield_class\n\t\t\t\t\t} );\n\n\t\t\t\t\t$.each( opt.select_options, function( k, v ) {\n\t\t\t\t\t\tvar selected = '';\n\t\t\t\t\t\tif( v == value ) {\n\t\t\t\t\t\t\tselected = 'selected=\"selected\"';\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif( value == v ) {\n\t\t\t\t\t\t\torig_option_value = k;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tform += _template( opt.select_option_form, {\n\t\t\t\t\t\t\tid\t\t\t: self.id,\n\t\t\t\t\t\t\toption_value: k,\n\t\t\t\t\t\t\toption_text\t: v,\n\t\t\t\t\t\t\tselected\t: selected\n\t\t\t\t\t\t} );\n\t\t\t\t\t} );\n\n\t\t\t\t\tform += _template( opt.stop_select_form, { } );\n\t\t\t\t} // select form\n\n\t\t\t\tform += _template( opt.form_buttons, {\n\t\t\t\t\tid\t\t\t\t\t: self.id,\n\t\t\t\t\tsavebutton_class\t: opt.savebutton_class,\n\t\t\t\t\tsavebutton_text\t\t: opt.savebutton_text,\n\t\t\t\t\tcancelbutton_class\t: opt.cancelbutton_class,\n\t\t\t\t\tcancelbutton_text\t: opt.cancelbutton_text\n\t\t\t\t} );\n\n\t\t\t\tform += _template( opt.stop_form, { } );\n\n\t\t\t\t$( self ).after( form );\n\t\t\t\t$( \"#editor-\" + self.id ).fadeIn( \"fast\" );\n\n\t\t\t\tif( opt.focus_edit ) {\n\t\t\t\t\t$( \"#edit-\" + self.id ).focus( );\n\t\t\t\t}\n\n\t\t\t\tif( opt.select_text ) {\n\t\t\t\t\t$( \"#edit-\" + self.id ).select( );\n\t\t\t\t}\n\n\t\t\t\t$( \"#cancel-\" + self.id ).bind( \"click\", function( e ) {\n\t\t\t\t\t_cancelEdit( self );\n\t\t\t\t} );\n\n\t\t\t\t$( \"#edit-\" + self.id ).keydown( function( e ) {\n\t\t\t\t\t// cancel\n\t\t\t\t\tif( e.which == 27 ) {\n\t\t\t\t\t\t_cancelEdit( self );\n\t\t\t\t\t}\n\n\t\t\t\t\t// save\n\t\t\t\t\tif( opt.form_type != \"textarea\" && e.which == 13 ) {\n\t\t\t\t\t\t_saveEdit( self, orig_option_value );\n\t\t\t\t\t}\n\t\t\t\t} );\n\n\t\t\t\t$( \"#save-\" + self.id ).bind( \"click\", function( e ) {\n\t\t\t\t\treturn _saveEdit( self, orig_option_value );\n\t\t\t\t} ); // save click\n\n                $( \"#edit-\" + self.id ).blur( function( e ) {\n                   return _saveEdit( self, orig_option_value );\n                } );\n\t\t\t} ); // this fadeOut\n\t\t} // function _editMode\n\n\t\tvar _template = function( template, values ) {\n\t\t\tvar replace = function( str, match ) {\n\t\t\t\treturn typeof values[match] === \"string\" || typeof values[match] === 'number' ? values[match] : str;\n\t\t\t};\n\t\t\treturn template.replace( /#\\{([^{}]*)}/g, replace );\n\t\t};\n\n\t\tvar _trim = function( str ) {\n\t\t\treturn str.replace(/^\\s\\s*/, '').replace(/\\s\\s*$/, '');\n\t\t}\n\n\t\tvar _cancelEdit = function( self ) {\n\t\t\t$( \"#editor-\" + self.id ).fadeOut( \"fast\" );\n\t\t\t$( \"#editor-\" + self.id ).remove( );\n\n\t\t\t$( self ).bind( opt.edit_event, function( e ) {\n\t\t\t\t_editMode( self );\n\t\t\t} );\n\n\t\t\t$( self ).removeClass( opt.mouseover_class );\n\t\t\t$( self ).fadeIn( \"fast\" );\n\t\t};\n\t\t\n\t\tvar _saveEdit = function( self, orig_option_value ) {\n\t\t\tvar orig_value = $( self ).html( );\n\t\t\tvar new_value = $( \"#edit-\" + self.id ).attr( \"value\" );\n\n\t\t\tif( orig_value == new_value ) {\n\t\t\t\t$( \"#editor-\" + self.id ).fadeOut( \"fast\" );\n\t\t\t\t$( \"#editor-\" + self.id ).remove( );\n\n\t\t\t\t$( self ).bind( opt.edit_event, function( e ) {\n\t\t\t\t\t_editMode( self );\n\t\t\t\t} );\n\n\t\t\t\t$( self ).removeClass( opt.mouseover_class );\n\t\t\t\t$( self ).fadeIn( \"fast\" );\n\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\t$( \"#editor-\" + self.id ).after( _template( opt.saving, {\n\t\t\t\tid\t\t\t: self.id,\n\t\t\t\tsaving_class: opt.saving_class,\n\t\t\t\tsaving_text\t: opt.saving_text\n\t\t\t} ) );\n\t\t\t$( \"#editor-\" + self.id ).fadeOut( \"fast\", function( ) {\n\t\t\t\t$( \"#saving-\" + self.id).fadeIn( \"fast\" );\n\t\t\t} );\n\n\t\t\tvar ajax_data = {\n\t\t\t\turl\t\t\t: location.href,\n\t\t\t\tid\t\t\t: self.id,\n\t\t\t\tform_type\t: opt.form_type,\n\t\t\t\torig_value\t: orig_value,\n\t\t\t\tnew_value\t: $( \"#edit-\" + self.id ).attr( \"value\" ),\n\t\t\t\tdata\t\t: opt.data\n\t\t\t}\n\n\t\t\tif( opt.form_type == 'select' ) {\n\t\t\t\tajax_data.orig_option_value = orig_option_value;\n\t\t\t\tajax_data.orig_option_text = orig_value;\n\t\t\t\tajax_data.new_option_text = $( \"#edit-option-\" + self.id + \"-\" + new_value ).html( );\n\t\t\t}\n\n\t\t\t$.ajax( {\n\t\t\t\turl\t\t: opt.save_url,\n\t\t\t\ttype\t: \"POST\",\n\t\t\t\tdataType: \"json\",\n\t\t\t\tdata\t: ajax_data,\n\t\t\t\tsuccess\t: function( data ) {\n\t\t\t\t\t$( \"#editor-\" + self.id ).fadeOut( \"fast\" );\n\t\t\t\t\t$( \"#editor-\" + self.id ).remove( );\n\n\t\t\t\t\tif( data.is_error == true ) {\n\t\t\t\t\t\topt.on_error( data.error_text );\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\t$( self ).html( data.html );\n\t\t\t\t\t}\n\n\t\t\t\t\t$( \"#saving-\" + self.id ).fadeOut( \"fast\" );\n\t\t\t\t\t$( \"#saving-\" + self.id ).remove( );\n\n\t\t\t\t\t$( self ).bind( opt.edit_event, function( e ) {\n\t\t\t\t\t\t_editMode( self );\n\t\t\t\t\t} );\n\n\t\t\t\t\t$( self ).addClass( opt.mouseover_class );\n\t\t\t\t\t$( self ).fadeIn( \"fast\" );\n\n\t\t\t\t\tif( opt.after_save != false ) {\n\t\t\t\t\t\topt.after_save( self );\n\t\t\t\t\t}\n\n\t\t\t\t\t$( self ).removeClass( opt.mouseover_class );\n\t\t\t\t} // success\n\t\t\t} ); // ajax\n\t\t}; // _saveEdit\n\n\n\t}; // inplaceEdit\n})( jQuery );\n"
  },
  {
    "path": "dirigible/shared/static/jquery/jquery-ui-1.8.10.custom.css",
    "content": "/*\n * jQuery UI CSS Framework 1.8.10\n *\n * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)\n * Dual licensed under the MIT or GPL Version 2 licenses.\n * http://jquery.org/license\n *\n * http://docs.jquery.com/UI/Theming/API\n */\n\n/* Layout helpers\n----------------------------------*/\n.ui-helper-hidden { display: none; }\n.ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,1px); }\n.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; }\n.ui-helper-clearfix:after { content: \".\"; display: block; height: 0; clear: both; visibility: hidden; }\n.ui-helper-clearfix { display: inline-block; }\n/* required comment for clearfix to work in Opera \\*/\n* html .ui-helper-clearfix { height:1%; }\n.ui-helper-clearfix { display:block; }\n/* end clearfix */\n.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }\n\n\n/* Interaction Cues\n----------------------------------*/\n.ui-state-disabled { cursor: default !important; }\n\n\n/* Icons\n----------------------------------*/\n\n/* states and images */\n.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; }\n\n\n/* Misc visuals\n----------------------------------*/\n\n/* Overlays */\n.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }\n\n\n/*\n * jQuery UI CSS Framework 1.8.10\n *\n * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)\n * Dual licensed under the MIT or GPL Version 2 licenses.\n * http://jquery.org/license\n *\n * http://docs.jquery.com/UI/Theming/API\n *\n * To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Helvetica,%20Arial,%20sans-serif&fwDefault=normal&fsDefault=100%&cornerRadius=4px&bgColorHeader=009ee0&bgTextureHeader=01_flat.png&bgImgOpacityHeader=0&borderColorHeader=009ee0&fcHeader=ffffff&iconColorHeader=ffffff&bgColorContent=ffffff&bgTextureContent=01_flat.png&bgImgOpacityContent=0&borderColorContent=009ee0&fcContent=000000&iconColorContent=ffffff&bgColorDefault=009ee0&bgTextureDefault=01_flat.png&bgImgOpacityDefault=0&borderColorDefault=009ee0&fcDefault=ffffff&iconColorDefault=ffffff&bgColorHover=009ee0&bgTextureHover=01_flat.png&bgImgOpacityHover=0&borderColorHover=009ee0&fcHover=ffffff&iconColorHover=ffffff&bgColorActive=ffffff&bgTextureActive=01_flat.png&bgImgOpacityActive=0&borderColorActive=009ee0&fcActive=009ee0&iconColorActive=ffffff&bgColorHighlight=ffffff&bgTextureHighlight=01_flat.png&bgImgOpacityHighlight=0&borderColorHighlight=009ee0&fcHighlight=000000&iconColorHighlight=009ee0&bgColorError=ffffff&bgTextureError=01_flat.png&bgImgOpacityError=0&borderColorError=ff0006&fcError=000000&iconColorError=ff0e00&bgColorOverlay=444444&bgTextureOverlay=01_flat.png&bgImgOpacityOverlay=0&opacityOverlay=60&bgColorShadow=aaaaaa&bgTextureShadow=01_flat.png&bgImgOpacityShadow=0&opacityShadow=60&thicknessShadow=0px&offsetTopShadow=0px&offsetLeftShadow=0px&cornerRadiusShadow=4px\n */\n\n\n/* Component containers\n----------------------------------*/\n.ui-widget { font-family: Helvetica, Arial, sans-serif; font-size: 100%; }\n.ui-widget .ui-widget { font-size: 1em; }\n.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Helvetica, Arial, sans-serif; font-size: 1em; }\n.ui-widget-content { border: 1px solid #009ee0; background: #ffffff url(images/ui-bg_flat_0_ffffff_40x100.png) 50% 50% repeat-x; color: #000000; }\n.ui-widget-content a { color: #000000; }\n.ui-widget-header { border: 1px solid #009ee0; background: #009ee0 url(images/ui-bg_flat_0_009ee0_40x100.png) 50% 50% repeat-x; color: #ffffff; font-weight: bold; }\n.ui-widget-header a { color: #ffffff; }\n\n/* Interaction states\n----------------------------------*/\n.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #009ee0; background: #009ee0 url(images/ui-bg_flat_0_009ee0_40x100.png) 50% 50% repeat-x; font-weight: normal; color: #ffffff; }\n.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #ffffff; text-decoration: none; }\n.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #009ee0; background: #009ee0 url(images/ui-bg_flat_0_009ee0_40x100.png) 50% 50% repeat-x; font-weight: normal; color: #ffffff; }\n.ui-state-hover a, .ui-state-hover a:hover { color: #ffffff; text-decoration: none; }\n.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #009ee0; background: #ffffff url(images/ui-bg_flat_0_ffffff_40x100.png) 50% 50% repeat-x; font-weight: normal; color: #009ee0; }\n.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #009ee0; text-decoration: none; }\n.ui-widget :active { outline: none; }\n\n/* Interaction Cues\n----------------------------------*/\n.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight  {border: 1px solid #009ee0; background: #ffffff url(images/ui-bg_flat_0_ffffff_40x100.png) 50% 50% repeat-x; color: #000000; }\n.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #000000; }\n.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #ff0006; background: #ffffff url(images/ui-bg_flat_0_ffffff_40x100.png) 50% 50% repeat-x; color: #000000; }\n.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #000000; }\n.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #000000; }\n.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; }\n.ui-priority-secondary, .ui-widget-content .ui-priority-secondary,  .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; }\n.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; }\n\n/* Icons\n----------------------------------*/\n\n/* states and images */\n.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_ffffff_256x240.png); }\n.ui-widget-content .ui-icon {background-image: url(images/ui-icons_ffffff_256x240.png); }\n.ui-widget-header .ui-icon {background-image: url(images/ui-icons_ffffff_256x240.png); }\n.ui-state-default .ui-icon { background-image: url(images/ui-icons_ffffff_256x240.png); }\n.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_ffffff_256x240.png); }\n.ui-state-active .ui-icon {background-image: url(images/ui-icons_ffffff_256x240.png); }\n.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_009ee0_256x240.png); }\n.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_ff0e00_256x240.png); }\n\n/* positioning */\n.ui-icon-carat-1-n { background-position: 0 0; }\n.ui-icon-carat-1-ne { background-position: -16px 0; }\n.ui-icon-carat-1-e { background-position: -32px 0; }\n.ui-icon-carat-1-se { background-position: -48px 0; }\n.ui-icon-carat-1-s { background-position: -64px 0; }\n.ui-icon-carat-1-sw { background-position: -80px 0; }\n.ui-icon-carat-1-w { background-position: -96px 0; }\n.ui-icon-carat-1-nw { background-position: -112px 0; }\n.ui-icon-carat-2-n-s { background-position: -128px 0; }\n.ui-icon-carat-2-e-w { background-position: -144px 0; }\n.ui-icon-triangle-1-n { background-position: 0 -16px; }\n.ui-icon-triangle-1-ne { background-position: -16px -16px; }\n.ui-icon-triangle-1-e { background-position: -32px -16px; }\n.ui-icon-triangle-1-se { background-position: -48px -16px; }\n.ui-icon-triangle-1-s { background-position: -64px -16px; }\n.ui-icon-triangle-1-sw { background-position: -80px -16px; }\n.ui-icon-triangle-1-w { background-position: -96px -16px; }\n.ui-icon-triangle-1-nw { background-position: -112px -16px; }\n.ui-icon-triangle-2-n-s { background-position: -128px -16px; }\n.ui-icon-triangle-2-e-w { background-position: -144px -16px; }\n.ui-icon-arrow-1-n { background-position: 0 -32px; }\n.ui-icon-arrow-1-ne { background-position: -16px -32px; }\n.ui-icon-arrow-1-e { background-position: -32px -32px; }\n.ui-icon-arrow-1-se { background-position: -48px -32px; }\n.ui-icon-arrow-1-s { background-position: -64px -32px; }\n.ui-icon-arrow-1-sw { background-position: -80px -32px; }\n.ui-icon-arrow-1-w { background-position: -96px -32px; }\n.ui-icon-arrow-1-nw { background-position: -112px -32px; }\n.ui-icon-arrow-2-n-s { background-position: -128px -32px; }\n.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }\n.ui-icon-arrow-2-e-w { background-position: -160px -32px; }\n.ui-icon-arrow-2-se-nw { background-position: -176px -32px; }\n.ui-icon-arrowstop-1-n { background-position: -192px -32px; }\n.ui-icon-arrowstop-1-e { background-position: -208px -32px; }\n.ui-icon-arrowstop-1-s { background-position: -224px -32px; }\n.ui-icon-arrowstop-1-w { background-position: -240px -32px; }\n.ui-icon-arrowthick-1-n { background-position: 0 -48px; }\n.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }\n.ui-icon-arrowthick-1-e { background-position: -32px -48px; }\n.ui-icon-arrowthick-1-se { background-position: -48px -48px; }\n.ui-icon-arrowthick-1-s { background-position: -64px -48px; }\n.ui-icon-arrowthick-1-sw { background-position: -80px -48px; }\n.ui-icon-arrowthick-1-w { background-position: -96px -48px; }\n.ui-icon-arrowthick-1-nw { background-position: -112px -48px; }\n.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }\n.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }\n.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }\n.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }\n.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }\n.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }\n.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }\n.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }\n.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }\n.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }\n.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }\n.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }\n.ui-icon-arrowreturn-1-w { background-position: -64px -64px; }\n.ui-icon-arrowreturn-1-n { background-position: -80px -64px; }\n.ui-icon-arrowreturn-1-e { background-position: -96px -64px; }\n.ui-icon-arrowreturn-1-s { background-position: -112px -64px; }\n.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }\n.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }\n.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }\n.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }\n.ui-icon-arrow-4 { background-position: 0 -80px; }\n.ui-icon-arrow-4-diag { background-position: -16px -80px; }\n.ui-icon-extlink { background-position: -32px -80px; }\n.ui-icon-newwin { background-position: -48px -80px; }\n.ui-icon-refresh { background-position: -64px -80px; }\n.ui-icon-shuffle { background-position: -80px -80px; }\n.ui-icon-transfer-e-w { background-position: -96px -80px; }\n.ui-icon-transferthick-e-w { background-position: -112px -80px; }\n.ui-icon-folder-collapsed { background-position: 0 -96px; }\n.ui-icon-folder-open { background-position: -16px -96px; }\n.ui-icon-document { background-position: -32px -96px; }\n.ui-icon-document-b { background-position: -48px -96px; }\n.ui-icon-note { background-position: -64px -96px; }\n.ui-icon-mail-closed { background-position: -80px -96px; }\n.ui-icon-mail-open { background-position: -96px -96px; }\n.ui-icon-suitcase { background-position: -112px -96px; }\n.ui-icon-comment { background-position: -128px -96px; }\n.ui-icon-person { background-position: -144px -96px; }\n.ui-icon-print { background-position: -160px -96px; }\n.ui-icon-trash { background-position: -176px -96px; }\n.ui-icon-locked { background-position: -192px -96px; }\n.ui-icon-unlocked { background-position: -208px -96px; }\n.ui-icon-bookmark { background-position: -224px -96px; }\n.ui-icon-tag { background-position: -240px -96px; }\n.ui-icon-home { background-position: 0 -112px; }\n.ui-icon-flag { background-position: -16px -112px; }\n.ui-icon-calendar { background-position: -32px -112px; }\n.ui-icon-cart { background-position: -48px -112px; }\n.ui-icon-pencil { background-position: -64px -112px; }\n.ui-icon-clock { background-position: -80px -112px; }\n.ui-icon-disk { background-position: -96px -112px; }\n.ui-icon-calculator { background-position: -112px -112px; }\n.ui-icon-zoomin { background-position: -128px -112px; }\n.ui-icon-zoomout { background-position: -144px -112px; }\n.ui-icon-search { background-position: -160px -112px; }\n.ui-icon-wrench { background-position: -176px -112px; }\n.ui-icon-gear { background-position: -192px -112px; }\n.ui-icon-heart { background-position: -208px -112px; }\n.ui-icon-star { background-position: -224px -112px; }\n.ui-icon-link { background-position: -240px -112px; }\n.ui-icon-cancel { background-position: 0 -128px; }\n.ui-icon-plus { background-position: -16px -128px; }\n.ui-icon-plusthick { background-position: -32px -128px; }\n.ui-icon-minus { background-position: -48px -128px; }\n.ui-icon-minusthick { background-position: -64px -128px; }\n.ui-icon-close { background-position: -80px -128px; }\n.ui-icon-closethick { background-position: -96px -128px; }\n.ui-icon-key { background-position: -112px -128px; }\n.ui-icon-lightbulb { background-position: -128px -128px; }\n.ui-icon-scissors { background-position: -144px -128px; }\n.ui-icon-clipboard { background-position: -160px -128px; }\n.ui-icon-copy { background-position: -176px -128px; }\n.ui-icon-contact { background-position: -192px -128px; }\n.ui-icon-image { background-position: -208px -128px; }\n.ui-icon-video { background-position: -224px -128px; }\n.ui-icon-script { background-position: -240px -128px; }\n.ui-icon-alert { background-position: 0 -144px; }\n.ui-icon-info { background-position: -16px -144px; }\n.ui-icon-notice { background-position: -32px -144px; }\n.ui-icon-help { background-position: -48px -144px; }\n.ui-icon-check { background-position: -64px -144px; }\n.ui-icon-bullet { background-position: -80px -144px; }\n.ui-icon-radio-off { background-position: -96px -144px; }\n.ui-icon-radio-on { background-position: -112px -144px; }\n.ui-icon-pin-w { background-position: -128px -144px; }\n.ui-icon-pin-s { background-position: -144px -144px; }\n.ui-icon-play { background-position: 0 -160px; }\n.ui-icon-pause { background-position: -16px -160px; }\n.ui-icon-seek-next { background-position: -32px -160px; }\n.ui-icon-seek-prev { background-position: -48px -160px; }\n.ui-icon-seek-end { background-position: -64px -160px; }\n.ui-icon-seek-start { background-position: -80px -160px; }\n/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */\n.ui-icon-seek-first { background-position: -80px -160px; }\n.ui-icon-stop { background-position: -96px -160px; }\n.ui-icon-eject { background-position: -112px -160px; }\n.ui-icon-volume-off { background-position: -128px -160px; }\n.ui-icon-volume-on { background-position: -144px -160px; }\n.ui-icon-power { background-position: 0 -176px; }\n.ui-icon-signal-diag { background-position: -16px -176px; }\n.ui-icon-signal { background-position: -32px -176px; }\n.ui-icon-battery-0 { background-position: -48px -176px; }\n.ui-icon-battery-1 { background-position: -64px -176px; }\n.ui-icon-battery-2 { background-position: -80px -176px; }\n.ui-icon-battery-3 { background-position: -96px -176px; }\n.ui-icon-circle-plus { background-position: 0 -192px; }\n.ui-icon-circle-minus { background-position: -16px -192px; }\n.ui-icon-circle-close { background-position: -32px -192px; }\n.ui-icon-circle-triangle-e { background-position: -48px -192px; }\n.ui-icon-circle-triangle-s { background-position: -64px -192px; }\n.ui-icon-circle-triangle-w { background-position: -80px -192px; }\n.ui-icon-circle-triangle-n { background-position: -96px -192px; }\n.ui-icon-circle-arrow-e { background-position: -112px -192px; }\n.ui-icon-circle-arrow-s { background-position: -128px -192px; }\n.ui-icon-circle-arrow-w { background-position: -144px -192px; }\n.ui-icon-circle-arrow-n { background-position: -160px -192px; }\n.ui-icon-circle-zoomin { background-position: -176px -192px; }\n.ui-icon-circle-zoomout { background-position: -192px -192px; }\n.ui-icon-circle-check { background-position: -208px -192px; }\n.ui-icon-circlesmall-plus { background-position: 0 -208px; }\n.ui-icon-circlesmall-minus { background-position: -16px -208px; }\n.ui-icon-circlesmall-close { background-position: -32px -208px; }\n.ui-icon-squaresmall-plus { background-position: -48px -208px; }\n.ui-icon-squaresmall-minus { background-position: -64px -208px; }\n.ui-icon-squaresmall-close { background-position: -80px -208px; }\n.ui-icon-grip-dotted-vertical { background-position: 0 -224px; }\n.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }\n.ui-icon-grip-solid-vertical { background-position: -32px -224px; }\n.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }\n.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }\n.ui-icon-grip-diagonal-se { background-position: -80px -224px; }\n\n\n/* Misc visuals\n----------------------------------*/\n\n/* Corner radius */\n.ui-corner-tl { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; border-top-left-radius: 4px; }\n.ui-corner-tr { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; border-top-right-radius: 4px; }\n.ui-corner-bl { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; }\n.ui-corner-br { -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; }\n.ui-corner-top { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; border-top-left-radius: 4px; -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; border-top-right-radius: 4px; }\n.ui-corner-bottom { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; }\n.ui-corner-right {  -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; border-top-right-radius: 4px; -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; }\n.ui-corner-left { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; border-top-left-radius: 4px; -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; }\n.ui-corner-all { -moz-border-radius: 4px; -webkit-border-radius: 4px; border-radius: 4px; }\n\n/* Overlays */\n.ui-widget-overlay { background: #444444 url(images/ui-bg_flat_0_444444_40x100.png) 50% 50% repeat-x; opacity: .60;filter:Alpha(Opacity=60); }\n.ui-widget-shadow { margin: 0px 0 0 0px; padding: 0px; background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .60;filter:Alpha(Opacity=60); -moz-border-radius: 4px; -webkit-border-radius: 4px; border-radius: 4px; }/*\n * jQuery UI Resizable 1.8.10\n *\n * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)\n * Dual licensed under the MIT or GPL Version 2 licenses.\n * http://jquery.org/license\n *\n * http://docs.jquery.com/UI/Resizable#theming\n */\n.ui-resizable { position: relative;}\n.ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block;}\n.ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; }\n.ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0; }\n.ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0; }\n.ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0; height: 100%; }\n.ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0; height: 100%; }\n.ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; }\n.ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; }\n.ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; }\n.ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}/*\n * jQuery UI Selectable 1.8.10\n *\n * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)\n * Dual licensed under the MIT or GPL Version 2 licenses.\n * http://jquery.org/license\n *\n * http://docs.jquery.com/UI/Selectable#theming\n */\n.ui-selectable-helper { position: absolute; z-index: 100; border:1px dotted black; }\n/*\n * jQuery UI Accordion 1.8.10\n *\n * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)\n * Dual licensed under the MIT or GPL Version 2 licenses.\n * http://jquery.org/license\n *\n * http://docs.jquery.com/UI/Accordion#theming\n */\n/* IE/Win - Fix animation bug - #4615 */\n.ui-accordion { width: 100%; }\n.ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; }\n.ui-accordion .ui-accordion-li-fix { display: inline; }\n.ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; }\n.ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em .7em; }\n.ui-accordion-icons .ui-accordion-header a { padding-left: 2.2em; }\n.ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; }\n.ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; zoom: 1; }\n.ui-accordion .ui-accordion-content-active { display: block; }\n/*\n * jQuery UI Autocomplete 1.8.10\n *\n * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)\n * Dual licensed under the MIT or GPL Version 2 licenses.\n * http://jquery.org/license\n *\n * http://docs.jquery.com/UI/Autocomplete#theming\n */\n.ui-autocomplete { position: absolute; cursor: default; }\t\n\n/* workarounds */\n* html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */\n\n/*\n * jQuery UI Menu 1.8.10\n *\n * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)\n * Dual licensed under the MIT or GPL Version 2 licenses.\n * http://jquery.org/license\n *\n * http://docs.jquery.com/UI/Menu#theming\n */\n.ui-menu {\n\tlist-style:none;\n\tpadding: 2px;\n\tmargin: 0;\n\tdisplay:block;\n\tfloat: left;\n}\n.ui-menu .ui-menu {\n\tmargin-top: -3px;\n}\n.ui-menu .ui-menu-item {\n\tmargin:0;\n\tpadding: 0;\n\tzoom: 1;\n\tfloat: left;\n\tclear: left;\n\twidth: 100%;\n}\n.ui-menu .ui-menu-item a {\n\ttext-decoration:none;\n\tdisplay:block;\n\tpadding:.2em .4em;\n\tline-height:1.5;\n\tzoom:1;\n}\n.ui-menu .ui-menu-item a.ui-state-hover,\n.ui-menu .ui-menu-item a.ui-state-active {\n\tfont-weight: normal;\n\tmargin: -1px;\n}\n/*\n * jQuery UI Button 1.8.10\n *\n * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)\n * Dual licensed under the MIT or GPL Version 2 licenses.\n * http://jquery.org/license\n *\n * http://docs.jquery.com/UI/Button#theming\n */\n.ui-button { display: inline-block; position: relative; padding: 0; margin-right: .1em; text-decoration: none !important; cursor: pointer; text-align: center; zoom: 1; overflow: visible; } /* the overflow property removes extra width in IE */\n.ui-button-icon-only { width: 2.2em; } /* to make room for the icon, a width needs to be set here */\nbutton.ui-button-icon-only { width: 2.4em; } /* button elements seem to need a little more width */\n.ui-button-icons-only { width: 3.4em; } \nbutton.ui-button-icons-only { width: 3.7em; } \n\n/*button text element */\n.ui-button .ui-button-text { display: block; line-height: 1.4;  }\n.ui-button-text-only .ui-button-text { padding: .4em 1em; }\n.ui-button-icon-only .ui-button-text, .ui-button-icons-only .ui-button-text { padding: .4em; text-indent: -9999999px; }\n.ui-button-text-icon-primary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 1em .4em 2.1em; }\n.ui-button-text-icon-secondary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 2.1em .4em 1em; }\n.ui-button-text-icons .ui-button-text { padding-left: 2.1em; padding-right: 2.1em; }\n/* no icon support for input elements, provide padding by default */\ninput.ui-button { padding: .4em 1em; }\n\n/*button icon element(s) */\n.ui-button-icon-only .ui-icon, .ui-button-text-icon-primary .ui-icon, .ui-button-text-icon-secondary .ui-icon, .ui-button-text-icons .ui-icon, .ui-button-icons-only .ui-icon { position: absolute; top: 50%; margin-top: -8px; }\n.ui-button-icon-only .ui-icon { left: 50%; margin-left: -8px; }\n.ui-button-text-icon-primary .ui-button-icon-primary, .ui-button-text-icons .ui-button-icon-primary, .ui-button-icons-only .ui-button-icon-primary { left: .5em; }\n.ui-button-text-icon-secondary .ui-button-icon-secondary, .ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }\n.ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }\n\n/*button sets*/\n.ui-buttonset { margin-right: 7px; }\n.ui-buttonset .ui-button { margin-left: 0; margin-right: -.3em; }\n\n/* workarounds */\nbutton.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra padding in Firefox */\n/*\n * jQuery UI Dialog 1.8.10\n *\n * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)\n * Dual licensed under the MIT or GPL Version 2 licenses.\n * http://jquery.org/license\n *\n * http://docs.jquery.com/UI/Dialog#theming\n */\n.ui-dialog { position: absolute; padding: .2em; width: 300px; overflow: hidden; }\n.ui-dialog .ui-dialog-titlebar { padding: .4em 1em; position: relative;  }\n.ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .1em 0; } \n.ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; }\n.ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; }\n.ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; }\n.ui-dialog .ui-dialog-content { position: relative; border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; }\n.ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; }\n.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { float: right; }\n.ui-dialog .ui-dialog-buttonpane button { margin: .5em .4em .5em 0; cursor: pointer; }\n.ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; }\n.ui-draggable .ui-dialog-titlebar { cursor: move; }\n/*\n * jQuery UI Slider 1.8.10\n *\n * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)\n * Dual licensed under the MIT or GPL Version 2 licenses.\n * http://jquery.org/license\n *\n * http://docs.jquery.com/UI/Slider#theming\n */\n.ui-slider { position: relative; text-align: left; }\n.ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; }\n.ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; background-position: 0 0; }\n\n.ui-slider-horizontal { height: .8em; }\n.ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; }\n.ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; }\n.ui-slider-horizontal .ui-slider-range-min { left: 0; }\n.ui-slider-horizontal .ui-slider-range-max { right: 0; }\n\n.ui-slider-vertical { width: .8em; height: 100px; }\n.ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; }\n.ui-slider-vertical .ui-slider-range { left: 0; width: 100%; }\n.ui-slider-vertical .ui-slider-range-min { bottom: 0; }\n.ui-slider-vertical .ui-slider-range-max { top: 0; }/*\n * jQuery UI Tabs 1.8.10\n *\n * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)\n * Dual licensed under the MIT or GPL Version 2 licenses.\n * http://jquery.org/license\n *\n * http://docs.jquery.com/UI/Tabs#theming\n */\n.ui-tabs { position: relative; padding: .2em; zoom: 1; } /* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as \"fixed\") */\n.ui-tabs .ui-tabs-nav { margin: 0; padding: .2em .2em 0; }\n.ui-tabs .ui-tabs-nav li { list-style: none; float: left; position: relative; top: 1px; margin: 0 .2em 1px 0; border-bottom: 0 !important; padding: 0; white-space: nowrap; }\n.ui-tabs .ui-tabs-nav li a { float: left; padding: .5em 1em; text-decoration: none; }\n.ui-tabs .ui-tabs-nav li.ui-tabs-selected { margin-bottom: 0; padding-bottom: 1px; }\n.ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; }\n.ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */\n.ui-tabs .ui-tabs-panel { display: block; border-width: 0; padding: 1em 1.4em; background: none; }\n.ui-tabs .ui-tabs-hide { display: none !important; }\n/*\n * jQuery UI Datepicker 1.8.10\n *\n * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)\n * Dual licensed under the MIT or GPL Version 2 licenses.\n * http://jquery.org/license\n *\n * http://docs.jquery.com/UI/Datepicker#theming\n */\n.ui-datepicker { width: 17em; padding: .2em .2em 0; display: none; }\n.ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; }\n.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; }\n.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; }\n.ui-datepicker .ui-datepicker-prev { left:2px; }\n.ui-datepicker .ui-datepicker-next { right:2px; }\n.ui-datepicker .ui-datepicker-prev-hover { left:1px; }\n.ui-datepicker .ui-datepicker-next-hover { right:1px; }\n.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px;  }\n.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; }\n.ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; }\n.ui-datepicker select.ui-datepicker-month-year {width: 100%;}\n.ui-datepicker select.ui-datepicker-month, \n.ui-datepicker select.ui-datepicker-year { width: 49%;}\n.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; }\n.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0;  }\n.ui-datepicker td { border: 0; padding: 1px; }\n.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; }\n.ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; }\n.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; }\n.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; }\n\n/* with multiple calendars */\n.ui-datepicker.ui-datepicker-multi { width:auto; }\n.ui-datepicker-multi .ui-datepicker-group { float:left; }\n.ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; }\n.ui-datepicker-multi-2 .ui-datepicker-group { width:50%; }\n.ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; }\n.ui-datepicker-multi-4 .ui-datepicker-group { width:25%; }\n.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; }\n.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; }\n.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; }\n.ui-datepicker-row-break { clear:both; width:100%; }\n\n/* RTL support */\n.ui-datepicker-rtl { direction: rtl; }\n.ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; }\n.ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; }\n.ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; }\n.ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; }\n.ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; }\n.ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; }\n.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; }\n.ui-datepicker-rtl .ui-datepicker-group { float:right; }\n.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; }\n.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; }\n\n/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */\n.ui-datepicker-cover {\n    display: none; /*sorry for IE5*/\n    display/**/: block; /*sorry for IE5*/\n    position: absolute; /*must have*/\n    z-index: -1; /*must have*/\n    filter: mask(); /*must have*/\n    top: -4px; /*must have*/\n    left: -4px; /*must have*/\n    width: 200px; /*must have*/\n    height: 200px; /*must have*/\n}/*\n * jQuery UI Progressbar 1.8.10\n *\n * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)\n * Dual licensed under the MIT or GPL Version 2 licenses.\n * http://jquery.org/license\n *\n * http://docs.jquery.com/UI/Progressbar#theming\n */\n.ui-progressbar { height:2em; text-align: left; }\n.ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; }"
  },
  {
    "path": "dirigible/shared/static/jquery/jquery.ajaxq-0.0.1.js",
    "content": "/*\r\n * jQuery AjaxQ - AJAX request queueing for jQuery\r\n *\r\n * Version: 0.0.1\r\n * Date: July 22, 2008\r\n *\r\n * Copyright (c) 2008 Oleg Podolsky (oleg.podolsky@gmail.com)\r\n * Licensed under the MIT (MIT-LICENSE.txt) license.\r\n *\r\n * http://plugins.jquery.com/project/ajaxq\r\n * http://code.google.com/p/jquery-ajaxq/\r\n */\r\n\r\njQuery.ajaxq = function (queue, options)\r\n{\r\n\t// Initialize storage for request queues if it's not initialized yet\r\n\tif (typeof document.ajaxq == \"undefined\") document.ajaxq = {q:{}, r:null};\r\n\r\n\t// Initialize current queue if it's not initialized yet\r\n\tif (typeof document.ajaxq.q[queue] == \"undefined\") document.ajaxq.q[queue] = [];\r\n\t\r\n\tif (typeof options != \"undefined\") // Request settings are given, enqueue the new request\r\n\t{\r\n\t\t// Copy the original options, because options.complete is going to be overridden\r\n\r\n\t\tvar optionsCopy = {};\r\n\t\tfor (var o in options) optionsCopy[o] = options[o];\r\n\t\toptions = optionsCopy;\r\n\t\t\r\n\t\t// Override the original callback\r\n\r\n\t\tvar originalCompleteCallback = options.complete;\r\n\r\n\t\toptions.complete = function (request, status)\r\n\t\t{\r\n\t\t\t// Dequeue the current request\r\n\t\t\tdocument.ajaxq.q[queue].shift ();\r\n\t\t\tdocument.ajaxq.r = null;\r\n\t\t\t\r\n\t\t\t// Run the original callback\r\n\t\t\tif (originalCompleteCallback) originalCompleteCallback (request, status);\r\n\r\n\t\t\t// Run the next request from the queue\r\n\t\t\tif (document.ajaxq.q[queue].length > 0) document.ajaxq.r = jQuery.ajax (document.ajaxq.q[queue][0]);\r\n\t\t};\r\n\r\n\t\t// Enqueue the request\r\n\t\tdocument.ajaxq.q[queue].push (options);\r\n\r\n\t\t// Also, if no request is currently running, start it\r\n\t\tif (document.ajaxq.q[queue].length == 1) document.ajaxq.r = jQuery.ajax (options);\r\n\t}\r\n\telse // No request settings are given, stop current request and clear the queue\r\n\t{\r\n\t\tif (document.ajaxq.r)\r\n\t\t{\r\n\t\t\tdocument.ajaxq.r.abort ();\r\n\t\t\tdocument.ajaxq.r = null;\r\n\t\t}\r\n\r\n\t\tdocument.ajaxq.q[queue] = [];\r\n\t}\r\n}"
  },
  {
    "path": "dirigible/shared/static/jquery/jquery.cookie.js",
    "content": "/**\n * Cookie plugin\n *\n * Copyright (c) 2006 Klaus Hartl (stilbuero.de)\n * Dual licensed under the MIT and GPL licenses:\n * http://www.opensource.org/licenses/mit-license.php\n * http://www.gnu.org/licenses/gpl.html\n *\n */\n\n/**\n * Create a cookie with the given name and value and other optional parameters.\n *\n * @example $.cookie('the_cookie', 'the_value');\n * @desc Set the value of a cookie.\n * @example $.cookie('the_cookie', 'the_value', { expires: 7, path: '/', domain: 'jquery.com', secure: true });\n * @desc Create a cookie with all available options.\n * @example $.cookie('the_cookie', 'the_value');\n * @desc Create a session cookie.\n * @example $.cookie('the_cookie', null);\n * @desc Delete a cookie by passing null as value. Keep in mind that you have to use the same path and domain\n *       used when the cookie was set.\n *\n * @param String name The name of the cookie.\n * @param String value The value of the cookie.\n * @param Object options An object literal containing key/value pairs to provide optional cookie attributes.\n * @option Number|Date expires Either an integer specifying the expiration date from now on in days or a Date object.\n *                             If a negative value is specified (e.g. a date in the past), the cookie will be deleted.\n *                             If set to null or omitted, the cookie will be a session cookie and will not be retained\n *                             when the the browser exits.\n * @option String path The value of the path atribute of the cookie (default: path of page that created the cookie).\n * @option String domain The value of the domain attribute of the cookie (default: domain of page that created the cookie).\n * @option Boolean secure If true, the secure attribute of the cookie will be set and the cookie transmission will\n *                        require a secure protocol (like HTTPS).\n * @type undefined\n *\n * @name $.cookie\n * @cat Plugins/Cookie\n * @author Klaus Hartl/klaus.hartl@stilbuero.de\n */\n\n/**\n * Get the value of a cookie with the given name.\n *\n * @example $.cookie('the_cookie');\n * @desc Get the value of a cookie.\n *\n * @param String name The name of the cookie.\n * @return The value of the cookie.\n * @type String\n *\n * @name $.cookie\n * @cat Plugins/Cookie\n * @author Klaus Hartl/klaus.hartl@stilbuero.de\n */\njQuery.cookie = function(name, value, options) {\n    if (typeof value != 'undefined') { // name and value given, set cookie\n        options = options || {};\n        if (value === null) {\n            value = '';\n            options.expires = -1;\n        }\n        var expires = '';\n        if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) {\n            var date;\n            if (typeof options.expires == 'number') {\n                date = new Date();\n                date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));\n            } else {\n                date = options.expires;\n            }\n            expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE\n        }\n        // CAUTION: Needed to parenthesize options.path and options.domain\n        // in the following expressions, otherwise they evaluate to undefined\n        // in the packed version for some reason...\n        var path = options.path ? '; path=' + (options.path) : '';\n        var domain = options.domain ? '; domain=' + (options.domain) : '';\n        var secure = options.secure ? '; secure' : '';\n        document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join('');\n    } else { // only name given, get cookie\n        var cookieValue = null;\n        if (document.cookie && document.cookie != '') {\n            var cookies = document.cookie.split(';');\n            for (var i = 0; i < cookies.length; i++) {\n                var cookie = jQuery.trim(cookies[i]);\n                // Does this cookie string begin with the name we want?\n                if (cookie.substring(0, name.length + 1) == (name + '=')) {\n                    cookieValue = decodeURIComponent(cookie.substring(name.length + 1));\n                    break;\n                }\n            }\n        }\n        return cookieValue;\n    }\n};"
  },
  {
    "path": "dirigible/shared/static/json/json2.js",
    "content": "/*\n    http://www.JSON.org/json2.js\n    2010-03-20\n\n    Public Domain.\n\n    NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.\n\n    See http://www.JSON.org/js.html\n\n\n    This code should be minified before deployment.\n    See http://javascript.crockford.com/jsmin.html\n\n    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO\n    NOT CONTROL.\n\n\n    This file creates a global JSON object containing two methods: stringify\n    and parse.\n\n        JSON.stringify(value, replacer, space)\n            value       any JavaScript value, usually an object or array.\n\n            replacer    an optional parameter that determines how object\n                        values are stringified for objects. It can be a\n                        function or an array of strings.\n\n            space       an optional parameter that specifies the indentation\n                        of nested structures. If it is omitted, the text will\n                        be packed without extra whitespace. If it is a number,\n                        it will specify the number of spaces to indent at each\n                        level. If it is a string (such as '\\t' or '&nbsp;'),\n                        it contains the characters used to indent at each level.\n\n            This method produces a JSON text from a JavaScript value.\n\n            When an object value is found, if the object contains a toJSON\n            method, its toJSON method will be called and the result will be\n            stringified. A toJSON method does not serialize: it returns the\n            value represented by the name/value pair that should be serialized,\n            or undefined if nothing should be serialized. The toJSON method\n            will be passed the key associated with the value, and this will be\n            bound to the value\n\n            For example, this would serialize Dates as ISO strings.\n\n                Date.prototype.toJSON = function (key) {\n                    function f(n) {\n                        // Format integers to have at least two digits.\n                        return n < 10 ? '0' + n : n;\n                    }\n\n                    return this.getUTCFullYear()   + '-' +\n                         f(this.getUTCMonth() + 1) + '-' +\n                         f(this.getUTCDate())      + 'T' +\n                         f(this.getUTCHours())     + ':' +\n                         f(this.getUTCMinutes())   + ':' +\n                         f(this.getUTCSeconds())   + 'Z';\n                };\n\n            You can provide an optional replacer method. It will be passed the\n            key and value of each member, with this bound to the containing\n            object. The value that is returned from your method will be\n            serialized. If your method returns undefined, then the member will\n            be excluded from the serialization.\n\n            If the replacer parameter is an array of strings, then it will be\n            used to select the members to be serialized. It filters the results\n            such that only members with keys listed in the replacer array are\n            stringified.\n\n            Values that do not have JSON representations, such as undefined or\n            functions, will not be serialized. Such values in objects will be\n            dropped; in arrays they will be replaced with null. You can use\n            a replacer function to replace those with JSON values.\n            JSON.stringify(undefined) returns undefined.\n\n            The optional space parameter produces a stringification of the\n            value that is filled with line breaks and indentation to make it\n            easier to read.\n\n            If the space parameter is a non-empty string, then that string will\n            be used for indentation. If the space parameter is a number, then\n            the indentation will be that many spaces.\n\n            Example:\n\n            text = JSON.stringify(['e', {pluribus: 'unum'}]);\n            // text is '[\"e\",{\"pluribus\":\"unum\"}]'\n\n\n            text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\\t');\n            // text is '[\\n\\t\"e\",\\n\\t{\\n\\t\\t\"pluribus\": \"unum\"\\n\\t}\\n]'\n\n            text = JSON.stringify([new Date()], function (key, value) {\n                return this[key] instanceof Date ?\n                    'Date(' + this[key] + ')' : value;\n            });\n            // text is '[\"Date(---current time---)\"]'\n\n\n        JSON.parse(text, reviver)\n            This method parses a JSON text to produce an object or array.\n            It can throw a SyntaxError exception.\n\n            The optional reviver parameter is a function that can filter and\n            transform the results. It receives each of the keys and values,\n            and its return value is used instead of the original value.\n            If it returns what it received, then the structure is not modified.\n            If it returns undefined then the member is deleted.\n\n            Example:\n\n            // Parse the text. Values that look like ISO date strings will\n            // be converted to Date objects.\n\n            myData = JSON.parse(text, function (key, value) {\n                var a;\n                if (typeof value === 'string') {\n                    a =\n/^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2}(?:\\.\\d*)?)Z$/.exec(value);\n                    if (a) {\n                        return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],\n                            +a[5], +a[6]));\n                    }\n                }\n                return value;\n            });\n\n            myData = JSON.parse('[\"Date(09/09/2001)\"]', function (key, value) {\n                var d;\n                if (typeof value === 'string' &&\n                        value.slice(0, 5) === 'Date(' &&\n                        value.slice(-1) === ')') {\n                    d = new Date(value.slice(5, -1));\n                    if (d) {\n                        return d;\n                    }\n                }\n                return value;\n            });\n\n\n    This is a reference implementation. You are free to copy, modify, or\n    redistribute.\n*/\n\n/*jslint evil: true, strict: false */\n\n/*members \"\", \"\\b\", \"\\t\", \"\\n\", \"\\f\", \"\\r\", \"\\\"\", JSON, \"\\\\\", apply,\n    call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,\n    getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,\n    lastIndex, length, parse, prototype, push, replace, slice, stringify,\n    test, toJSON, toString, valueOf\n*/\n\n\n// Create a JSON object only if one does not already exist. We create the\n// methods in a closure to avoid creating global variables.\n\nif (!this.JSON) {\n    this.JSON = {};\n}\n\n(function () {\n\n    function f(n) {\n        // Format integers to have at least two digits.\n        return n < 10 ? '0' + n : n;\n    }\n\n    if (typeof Date.prototype.toJSON !== 'function') {\n\n        Date.prototype.toJSON = function (key) {\n\n            return isFinite(this.valueOf()) ?\n                   this.getUTCFullYear()   + '-' +\n                 f(this.getUTCMonth() + 1) + '-' +\n                 f(this.getUTCDate())      + 'T' +\n                 f(this.getUTCHours())     + ':' +\n                 f(this.getUTCMinutes())   + ':' +\n                 f(this.getUTCSeconds())   + 'Z' : null;\n        };\n\n        String.prototype.toJSON =\n        Number.prototype.toJSON =\n        Boolean.prototype.toJSON = function (key) {\n            return this.valueOf();\n        };\n    }\n\n    var cx = /[\\u0000\\u00ad\\u0600-\\u0604\\u070f\\u17b4\\u17b5\\u200c-\\u200f\\u2028-\\u202f\\u2060-\\u206f\\ufeff\\ufff0-\\uffff]/g,\n        escapable = /[\\\\\\\"\\x00-\\x1f\\x7f-\\x9f\\u00ad\\u0600-\\u0604\\u070f\\u17b4\\u17b5\\u200c-\\u200f\\u2028-\\u202f\\u2060-\\u206f\\ufeff\\ufff0-\\uffff]/g,\n        gap,\n        indent,\n        meta = {    // table of character substitutions\n            '\\b': '\\\\b',\n            '\\t': '\\\\t',\n            '\\n': '\\\\n',\n            '\\f': '\\\\f',\n            '\\r': '\\\\r',\n            '\"' : '\\\\\"',\n            '\\\\': '\\\\\\\\'\n        },\n        rep;\n\n\n    function quote(string) {\n\n// If the string contains no control characters, no quote characters, and no\n// backslash characters, then we can safely slap some quotes around it.\n// Otherwise we must also replace the offending characters with safe escape\n// sequences.\n\n        escapable.lastIndex = 0;\n        return escapable.test(string) ?\n            '\"' + string.replace(escapable, function (a) {\n                var c = meta[a];\n                return typeof c === 'string' ? c :\n                    '\\\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);\n            }) + '\"' :\n            '\"' + string + '\"';\n    }\n\n\n    function str(key, holder) {\n\n// Produce a string from holder[key].\n\n        var i,          // The loop counter.\n            k,          // The member key.\n            v,          // The member value.\n            length,\n            mind = gap,\n            partial,\n            value = holder[key];\n\n// If the value has a toJSON method, call it to obtain a replacement value.\n\n        if (value && typeof value === 'object' &&\n                typeof value.toJSON === 'function') {\n            value = value.toJSON(key);\n        }\n\n// If we were called with a replacer function, then call the replacer to\n// obtain a replacement value.\n\n        if (typeof rep === 'function') {\n            value = rep.call(holder, key, value);\n        }\n\n// What happens next depends on the value's type.\n\n        switch (typeof value) {\n        case 'string':\n            return quote(value);\n\n        case 'number':\n\n// JSON numbers must be finite. Encode non-finite numbers as null.\n\n            return isFinite(value) ? String(value) : 'null';\n\n        case 'boolean':\n        case 'null':\n\n// If the value is a boolean or null, convert it to a string. Note:\n// typeof null does not produce 'null'. The case is included here in\n// the remote chance that this gets fixed someday.\n\n            return String(value);\n\n// If the type is 'object', we might be dealing with an object or an array or\n// null.\n\n        case 'object':\n\n// Due to a specification blunder in ECMAScript, typeof null is 'object',\n// so watch out for that case.\n\n            if (!value) {\n                return 'null';\n            }\n\n// Make an array to hold the partial results of stringifying this object value.\n\n            gap += indent;\n            partial = [];\n\n// Is the value an array?\n\n            if (Object.prototype.toString.apply(value) === '[object Array]') {\n\n// The value is an array. Stringify every element. Use null as a placeholder\n// for non-JSON values.\n\n                length = value.length;\n                for (i = 0; i < length; i += 1) {\n                    partial[i] = str(i, value) || 'null';\n                }\n\n// Join all of the elements together, separated with commas, and wrap them in\n// brackets.\n\n                v = partial.length === 0 ? '[]' :\n                    gap ? '[\\n' + gap +\n                            partial.join(',\\n' + gap) + '\\n' +\n                                mind + ']' :\n                          '[' + partial.join(',') + ']';\n                gap = mind;\n                return v;\n            }\n\n// If the replacer is an array, use it to select the members to be stringified.\n\n            if (rep && typeof rep === 'object') {\n                length = rep.length;\n                for (i = 0; i < length; i += 1) {\n                    k = rep[i];\n                    if (typeof k === 'string') {\n                        v = str(k, value);\n                        if (v) {\n                            partial.push(quote(k) + (gap ? ': ' : ':') + v);\n                        }\n                    }\n                }\n            } else {\n\n// Otherwise, iterate through all of the keys in the object.\n\n                for (k in value) {\n                    if (Object.hasOwnProperty.call(value, k)) {\n                        v = str(k, value);\n                        if (v) {\n                            partial.push(quote(k) + (gap ? ': ' : ':') + v);\n                        }\n                    }\n                }\n            }\n\n// Join all of the member texts together, separated with commas,\n// and wrap them in braces.\n\n            v = partial.length === 0 ? '{}' :\n                gap ? '{\\n' + gap + partial.join(',\\n' + gap) + '\\n' +\n                        mind + '}' : '{' + partial.join(',') + '}';\n            gap = mind;\n            return v;\n        }\n    }\n\n// If the JSON object does not yet have a stringify method, give it one.\n\n    if (typeof JSON.stringify !== 'function') {\n        JSON.stringify = function (value, replacer, space) {\n\n// The stringify method takes a value and an optional replacer, and an optional\n// space parameter, and returns a JSON text. The replacer can be a function\n// that can replace values, or an array of strings that will select the keys.\n// A default replacer method can be provided. Use of the space parameter can\n// produce text that is more easily readable.\n\n            var i;\n            gap = '';\n            indent = '';\n\n// If the space parameter is a number, make an indent string containing that\n// many spaces.\n\n            if (typeof space === 'number') {\n                for (i = 0; i < space; i += 1) {\n                    indent += ' ';\n                }\n\n// If the space parameter is a string, it will be used as the indent string.\n\n            } else if (typeof space === 'string') {\n                indent = space;\n            }\n\n// If there is a replacer, it must be a function or an array.\n// Otherwise, throw an error.\n\n            rep = replacer;\n            if (replacer && typeof replacer !== 'function' &&\n                    (typeof replacer !== 'object' ||\n                     typeof replacer.length !== 'number')) {\n                throw new Error('JSON.stringify');\n            }\n\n// Make a fake root object containing our value under the key of ''.\n// Return the result of stringifying the value.\n\n            return str('', {'': value});\n        };\n    }\n\n\n// If the JSON object does not yet have a parse method, give it one.\n\n    if (typeof JSON.parse !== 'function') {\n        JSON.parse = function (text, reviver) {\n\n// The parse method takes a text and an optional reviver function, and returns\n// a JavaScript value if the text is a valid JSON text.\n\n            var j;\n\n            function walk(holder, key) {\n\n// The walk method is used to recursively walk the resulting structure so\n// that modifications can be made.\n\n                var k, v, value = holder[key];\n                if (value && typeof value === 'object') {\n                    for (k in value) {\n                        if (Object.hasOwnProperty.call(value, k)) {\n                            v = walk(value, k);\n                            if (v !== undefined) {\n                                value[k] = v;\n                            } else {\n                                delete value[k];\n                            }\n                        }\n                    }\n                }\n                return reviver.call(holder, key, value);\n            }\n\n\n// Parsing happens in four stages. In the first stage, we replace certain\n// Unicode characters with escape sequences. JavaScript handles many characters\n// incorrectly, either silently deleting them, or treating them as line endings.\n\n            text = String(text);\n            cx.lastIndex = 0;\n            if (cx.test(text)) {\n                text = text.replace(cx, function (a) {\n                    return '\\\\u' +\n                        ('0000' + a.charCodeAt(0).toString(16)).slice(-4);\n                });\n            }\n\n// In the second stage, we run the text against regular expressions that look\n// for non-JSON patterns. We are especially concerned with '()' and 'new'\n// because they can cause invocation, and '=' because it can cause mutation.\n// But just to be safe, we want to reject all unexpected forms.\n\n// We split the second stage into 4 regexp operations in order to work around\n// crippling inefficiencies in IE's and Safari's regexp engines. First we\n// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we\n// replace all simple value tokens with ']' characters. Third, we delete all\n// open brackets that follow a colon or comma or that begin the text. Finally,\n// we look to see that the remaining characters are only whitespace or ']' or\n// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.\n\n            if (/^[\\],:{}\\s]*$/.\ntest(text.replace(/\\\\(?:[\"\\\\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').\nreplace(/\"[^\"\\\\\\n\\r]*\"|true|false|null|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?/g, ']').\nreplace(/(?:^|:|,)(?:\\s*\\[)+/g, ''))) {\n\n// In the third stage we use the eval function to compile the text into a\n// JavaScript structure. The '{' operator is subject to a syntactic ambiguity\n// in JavaScript: it can begin a block or an object literal. We wrap the text\n// in parens to eliminate the ambiguity.\n\n                j = eval('(' + text + ')');\n\n// In the optional fourth stage, we recursively walk the new structure, passing\n// each name/value pair to a reviver function for possible transformation.\n\n                return typeof reviver === 'function' ?\n                    walk({'': j}, '') : j;\n            }\n\n// If the text is not JSON parseable, then a SyntaxError is thrown.\n\n            throw new SyntaxError('JSON.parse');\n        };\n    }\n}());\n\n"
  },
  {
    "path": "dirigible/shared/static/robots.txt",
    "content": "User-agent: *\nDisallow: /user/\nDisallow: /feedback/submit\n"
  },
  {
    "path": "dirigible/shared/static/slickgrid/MIT-LICENSE.txt",
    "content": "Copyright (c) 2010 Michael Leibman, http://github.com/mleibman/slickgrid\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE."
  },
  {
    "path": "dirigible/shared/static/slickgrid/slick.cellrangedecorator.js",
    "content": "(function($) {\n    // register namespace\n    $.extend(true, window, {\n        \"Slick\": {\n            \"CellRangeDecorator\":   CellRangeDecorator\n        }\n    });\n\n    /***\n     * Displays an overlay on top of a given cell range.\n     *\n     * TODO:\n     * Currently, it blocks mouse events to DOM nodes behind it.\n     * Use FF and WebKit-specific \"pointer-events\" CSS style, or some kind of event forwarding.\n     * Could also construct the borders separately using 4 individual DIVs.\n     *\n     * @param {Grid} grid\n     * @param {Object} options\n     */\n    function CellRangeDecorator(grid, options) {\n        var _elem;\n        var _defaults = {\n            selectionCss: {\n                \"zIndex\": \"9999\",\n                \"border\": \"2px dashed red\"\n            }\n        };\n\n        options = $.extend(true, {}, _defaults, options);\n\n\n        function show(range) {\n            if (!_elem) {\n                _elem = $(\"<div></div>\", {css: options.selectionCss})\n                            .css(\"position\", \"absolute\")\n                            .appendTo(grid.getCanvasNode());\n\n            }\n\n            var from = grid.getCellNodeBox(range.fromRow,range.fromCell);\n            var to = grid.getCellNodeBox(range.toRow,range.toCell);\n\n            _elem.css({\n                top: from.top - 1,\n                left: from.left - 1,\n                height: to.bottom - from.top - 2,\n                width: to.right - from.left - 2\n            });\n\n            return _elem;\n        }\n\n        function hide() {\n            if (_elem) {\n                _elem.remove();\n                _elem = null;\n            }\n        }\n\n        $.extend(this, {\n            \"show\":     show,\n            \"hide\":     hide\n        });\n    }\n})(jQuery);"
  },
  {
    "path": "dirigible/shared/static/slickgrid/slick.cellrangeselector.js",
    "content": "(function($) {\n    // register namespace\n    $.extend(true, window, {\n        \"Slick\": {\n            \"CellRangeSelector\":   CellRangeSelector\n        }\n    });\n\n\n    function CellRangeSelector(options) {\n        var _grid;\n        var _canvas;\n        var _dragging;\n        var _decorator;\n        var _self = this;\n        var _defaults = {\n            selectionCss: {\n                \"border\": \"2px dashed blue\"\n            }\n        };\n\n\n        function init(grid) {\n            options = $.extend(true, {}, _defaults, options);\n            _decorator = new Slick.CellRangeDecorator(grid, options);\n            _grid = grid;\n            _canvas = _grid.getCanvasNode();\n            _grid.onDragStart.subscribe(handleDragStart);\n            _grid.onDrag.subscribe(handleDrag);\n            _grid.onDragEnd.subscribe(handleDragEnd);\n        }\n\n        function destroy() {\n            _grid.onDragStart.unsubscribe(handleDragStart);\n            _grid.onDrag.unsubscribe(handleDrag);\n            _grid.onDragEnd.unsubscribe(handleDragEnd);\n        }\n\n        function handleDragStart(e,dd) {\n            var cell = _grid.getCellFromEvent(e);\n            if (_self.onBeforeCellRangeSelected.notify(cell) !== false) {\n                if (_grid.canCellBeSelected(cell.row,cell.cell)) {\n                    _dragging = true;\n                    e.stopImmediatePropagation();\n                }\n            }\n            if (!_dragging) {\n                return;\n            }\n\n            var start = _grid.getCellFromPoint(\n                    dd.startX - $(_canvas).offset().left,\n                    dd.startY - $(_canvas).offset().top);\n\n            dd.range = {start:start,end:{}};\n\n            return _decorator.show(new Slick.Range(start.row,start.cell));\n        }\n\n        function handleDrag(e,dd) {\n            if (!_dragging) {\n                return;\n            }\n            e.stopImmediatePropagation();\n\n            var end = _grid.getCellFromPoint(\n                    e.pageX - $(_canvas).offset().left,\n                    e.pageY - $(_canvas).offset().top);\n\n            if (!_grid.canCellBeSelected(end.row,end.cell)) {\n                return;\n            }\n\n            dd.range.end = end;\n            _decorator.show(new Slick.Range(dd.range.start.row,dd.range.start.cell,end.row,end.cell));\n        }\n\n        function handleDragEnd(e,dd) {\n            if (!_dragging) {\n                return;\n            }\n\n            _dragging = false;\n            e.stopImmediatePropagation();\n\n            _decorator.hide();\n            _self.onCellRangeSelected.notify({\n                range:  new Slick.Range(\n                            dd.range.start.row,\n                            dd.range.start.cell,\n                            dd.range.end.row,\n                            dd.range.end.cell\n                            )\n            });\n        }\n\n        $.extend(this, {\n            \"init\":                         init,\n            \"destroy\":                      destroy,\n\n            \"onBeforeCellRangeSelected\":    new Slick.Event(),\n            \"onCellRangeSelected\":          new Slick.Event()\n        });\n    }\n})(jQuery);"
  },
  {
    "path": "dirigible/shared/static/slickgrid/slick.cellselectionmodel.js",
    "content": "(function($) {\n    // register namespace\n    $.extend(true, window, {\n        \"Slick\": {\n            \"CellSelectionModel\":   CellSelectionModel\n        }\n    });\n\n\n    function CellSelectionModel(options) {\n        var _grid;\n        var _canvas;\n        var _ranges = [];\n        var _self = this;\n        var _selector = new Slick.CellRangeSelector({\n            \"selectionCss\": {\n                \"border\": \"2px solid black\"\n            }\n        });\n        var _options;\n        var _defaults = {\n            selectActiveCell: true\n        };\n\n\n        function init(grid) {\n            _options = $.extend(true, {}, _defaults, options);\n            _grid = grid;\n            _canvas = _grid.getCanvasNode();\n            _grid.onActiveCellChanged.subscribe(handleActiveCellChange);\n            grid.registerPlugin(_selector);\n            _selector.onCellRangeSelected.subscribe(handleCellRangeSelected);\n            _selector.onBeforeCellRangeSelected.subscribe(handleBeforeCellRangeSelected);\n        }\n\n        function destroy() {\n            _grid.onActiveCellChanged.unsubscribe(handleActiveCellChange);\n            _selector.onCellRangeSelected.unsubscribe(handleCellRangeSelected);\n            _selector.onBeforeCellRangeSelected.unsubscribe(handleBeforeCellRangeSelected);\n            _grid.unregisterPlugin(_selector);\n        }\n\n        function removeInvalidRanges(ranges) {\n            var result = [];\n\n            for (var i = 0; i < ranges.length; i++) {\n                var r = ranges[i];\n                if (_grid.canCellBeSelected(r.fromRow,r.fromCell) && _grid.canCellBeSelected(r.toRow,r.toCell)) {\n                    result.push(r);\n                }\n            }\n\n            return result;\n        }\n\n        function setSelectedRanges(ranges) {\n            _ranges = removeInvalidRanges(ranges);\n            _self.onSelectedRangesChanged.notify(_ranges);\n        }\n\n        function getSelectedRanges() {\n            return _ranges;\n        }\n\n        function handleBeforeCellRangeSelected(e, args) {\n            if (_grid.getEditorLock().isActive()) {\n                e.stopPropagation();\n                return false;\n            }\n        }\n\n        function handleCellRangeSelected(e, args) {\n            setSelectedRanges([args.range]);\n        }\n\n        function handleActiveCellChange(e, args) {\n            if (_options.selectActiveCell) {\n                setSelectedRanges([new Slick.Range(args.row,args.cell)]);\n            }\n        }\n\n        $.extend(this, {\n            \"getSelectedRanges\":            getSelectedRanges,\n            \"setSelectedRanges\":            setSelectedRanges,\n\n            \"init\":                         init,\n            \"destroy\":                      destroy,\n\n            \"onSelectedRangesChanged\":      new Slick.Event()\n        });\n    }\n})(jQuery);"
  },
  {
    "path": "dirigible/shared/static/slickgrid/slick.core.js",
    "content": "/***\n * Contains core SlickGrid classes.\n * @module Core\n * @namespace Slick\n */\n\n(function($) {\n    // register namespace\n    $.extend(true, window, {\n        \"Slick\": {\n            \"Event\":        Event,\n            \"EventData\":    EventData,\n            \"Range\":        Range,\n            \"NonDataRow\":   NonDataItem,\n            \"Group\":        Group,\n            \"GroupTotals\":  GroupTotals,\n            \"EditorLock\":   EditorLock,\n\n            /***\n             * A global singleton editor lock.\n             * @class GlobalEditorLock\n             * @static\n             * @constructor\n             */\n            \"GlobalEditorLock\": new EditorLock()\n        }\n    });\n\n    /***\n     * An event object for passing data to event handlers and letting them control propagation.\n     * <p>This is pretty much identical to how W3C and jQuery implement events.</p>\n     * @class EventData\n     * @constructor\n     */\n    function EventData() {\n        var isPropagationStopped = false;\n        var isImmediatePropagationStopped = false;\n\n        /***\n         * Stops event from propagating up the DOM tree.\n         * @method stopPropagation\n         */\n        this.stopPropagation = function() {\n            isPropagationStopped = true;\n        };\n\n        /***\n         * Returns whether stopPropagation was called on this event object.\n         * @method isPropagationStopped\n         * @return {Boolean}\n         */\n        this.isPropagationStopped = function() {\n            return isPropagationStopped;\n        };\n\n        /***\n         * Prevents the rest of the handlers from being executed.\n         * @method stopImmediatePropagation\n         */\n        this.stopImmediatePropagation = function() {\n            isImmediatePropagationStopped = true;\n        };\n\n        /***\n         * Returns whether stopImmediatePropagation was called on this event object.\\\n         * @method isImmediatePropagationStopped\n         * @return {Boolean}\n         */\n        this.isImmediatePropagationStopped = function() {\n            return isImmediatePropagationStopped;\n        }\n    }\n\n    /***\n     * A simple publisher-subscriber implementation.\n     * @class Event\n     * @constructor\n     */\n    function Event() {\n        var handlers = [];\n\n        /***\n         * Adds an event handler to be called when the event is fired.\n         * <p>Event handler will receive two arguments - an <code>EventData</code> and the <code>data</code>\n         * object the event was fired with.<p>\n         * @method subscribe\n         * @param fn {Function} Event handler.\n         */\n        this.subscribe = function(fn) {\n            handlers.push(fn);\n        };\n\n        /***\n         * Removes an event handler added with <code>subscribe(fn)</code>.\n         * @method unsubscribe\n         * @param fn {Function} Event handler to be removed.\n         */\n        this.unsubscribe = function(fn) {\n            for (var i = handlers.length - 1; i >= 0; i--) {\n                if (handlers[i] === fn) {\n                    handlers.splice(i, 1);\n                }\n            }\n        };\n\n        /***\n         * Fires an event notifying all subscribers.\n         * @method notify\n         * @param args {Object} Additional data object to be passed to all handlers.\n         * @param e {EventData}\n         *      Optional.\n         *      An <code>EventData</code> object to be passed to all handlers.\n         *      For DOM events, an existing W3C/jQuery event object can be passed in.\n         * @param scope {Object}\n         *      Optional.\n         *      The scope (\"this\") within which the handler will be executed.\n         *      If not specified, the scope will be set to the <code>Event</code> instance.\n         */\n        this.notify = function(args, e, scope) {\n            e = e || new EventData();\n            scope = scope || this;\n\n            var returnValue;\n            for (var i = 0; i < handlers.length && !(e.isPropagationStopped() || e.isImmediatePropagationStopped()); i++) {\n                returnValue = handlers[i].call(scope, e, args);\n            }\n\n            return returnValue;\n        };\n    }\n\n    /***\n     * A structure containing a range of cells.\n     * @class Range\n     * @constructor\n     * @param fromRow {Integer} Starting row.\n     * @param fromCell {Integer} Starting cell.\n     * @param toRow {Integer} Optional. Ending row. Defaults to <code>fromRow</code>.\n     * @param toCell {Integer} Optional. Ending cell. Defaults to <code>fromCell</code>.\n     */\n    function Range(fromRow, fromCell, toRow, toCell) {\n        if (toRow === undefined && toCell === undefined) {\n            toRow = fromRow;\n            toCell = fromCell;\n        }\n\n        /***\n         * @property fromRow\n         * @type {Integer}\n         */\n        this.fromRow = Math.min(fromRow, toRow);\n\n        /***\n         * @property fromCell\n         * @type {Integer}\n         */\n        this.fromCell = Math.min(fromCell, toCell);\n\n        /***\n         * @property toRow\n         * @type {Integer}\n         */\n        this.toRow = Math.max(fromRow, toRow);\n\n        /***\n         * @property toCell\n         * @type {Integer}\n         */\n        this.toCell = Math.max(fromCell, toCell);\n\n        /***\n         * Returns whether a range represents a single row.\n         * @method isSingleRow\n         * @return {Boolean}\n         */\n        this.isSingleRow = function() {\n            return this.fromRow == this.toRow;\n        };\n\n        /***\n         * Returns whether a range represents a single cell.\n         * @method isSingleCell\n         * @return {Boolean}\n         */\n        this.isSingleCell = function() {\n            return this.fromRow == this.toRow && this.fromCell == this.toCell;\n        };\n\n        /***\n         * Returns whether a range contains a given cell.\n         * @method contains\n         * @param row {Integer}\n         * @param cell {Integer}\n         * @return {Boolean}\n         */\n        this.contains = function(row, cell) {\n            return row >= this.fromRow && row <= this.toRow &&\n                   cell >= this.fromCell && cell <= this.toCell;\n        };\n\n        /***\n         * Returns a readable representation of a range.\n         * @method toString\n         * @return {String}\n         */\n        this.toString = function() {\n            if (this.isSingleCell()) {\n                return \"(\" + this.fromRow + \":\" + this.fromCell + \")\";\n            }\n            else {\n                return \"(\" + this.fromRow + \":\" + this.fromCell + \" - \" + this.toRow + \":\" + this.toCell + \")\";\n            }\n        }\n    }\n\n\n    /***\n     * A base class that all special / non-data rows (like Group and GroupTotals) derive from.\n     * @class NonDataItem\n     * @constructor\n     */\n    function NonDataItem() {\n        this.__nonDataRow = true;\n    }\n\n\n    /***\n     * Information about a group of rows.\n     * @class Group\n     * @extends Slick.NonDataItem\n     * @constructor\n     */\n    function Group() {\n        this.__group = true;\n        this.__updated = false;\n\n        /***\n         * Number of rows in the group.\n         * @property count\n         * @type {Integer}\n         */\n        this.count = 0;\n\n        /***\n         * Grouping value.\n         * @property value\n         * @type {Object}\n         */\n        this.value = null;\n\n        /***\n         * Formatted display value of the group.\n         * @property title\n         * @type {String}\n         */\n        this.title = null;\n\n        /***\n         * Whether a group is collapsed.\n         * @property collapsed\n         * @type {Boolean}\n         */\n        this.collapsed = false;\n\n        /***\n         * GroupTotals, if any.\n         * @property totals\n         * @type {GroupTotals}\n         */\n        this.totals = null;\n    }\n\n    Group.prototype = new NonDataItem();\n\n    /***\n     * Compares two Group instances.\n     * @method equals\n     * @return {Boolean}\n     * @param group {Group} Group instance to compare to.\n     */\n    Group.prototype.equals = function(group) {\n        return this.value === group.value &&\n               this.count === group.count &&\n               this.collapsed === group.collapsed;\n    };\n\n    /***\n     * Information about group totals.\n     * An instance of GroupTotals will be created for each totals row and passed to the aggregators\n     * so that they can store arbitrary data in it.  That data can later be accessed by group totals\n     * formatters during the display.\n     * @class GroupTotals\n     * @extends Slick.NonDataItem\n     * @constructor\n     */\n    function GroupTotals() {\n        this.__groupTotals = true;\n\n        /***\n         * Parent Group.\n         * @param group\n         * @type {Group}\n         */\n        this.group = null;\n    }\n\n    GroupTotals.prototype = new NonDataItem();\n\n    /***\n     * A locking helper to track the active edit controller and ensure that only a single controller\n     * can be active at a time.  This prevents a whole class of state and validation synchronization\n     * issues.  An edit controller (such as SlickGrid) can query if an active edit is in progress\n     * and attempt a commit or cancel before proceeding.\n     * @class EditorLock\n     * @constructor\n     */\n    function EditorLock() {\n        var activeEditController = null;\n\n        /***\n         * Returns true if a specified edit controller is active (has the edit lock).\n         * If the parameter is not specified, returns true if any edit controller is active.\n         * @method isActive\n         * @param editController {EditController}\n         * @return {Boolean}\n         */\n        this.isActive = function(editController) {\n            return (editController ? activeEditController === editController : activeEditController !== null);\n        };\n\n        /***\n         * Sets the specified edit controller as the active edit controller (acquire edit lock).\n         * If another edit controller is already active, and exception will be thrown.\n         * @method activate\n         * @param editController {EditController} edit controller acquiring the lock\n         */\n        this.activate = function(editController) {\n            if (editController === activeEditController) { // already activated?\n                return;\n            }\n            if (activeEditController !== null) {\n                throw \"SlickGrid.EditorLock.activate: an editController is still active, can't activate another editController\";\n            }\n            if (!editController.commitCurrentEdit) {\n                throw \"SlickGrid.EditorLock.activate: editController must implement .commitCurrentEdit()\";\n            }\n            if (!editController.cancelCurrentEdit) {\n                throw \"SlickGrid.EditorLock.activate: editController must implement .cancelCurrentEdit()\";\n            }\n            activeEditController = editController;\n        };\n\n        /***\n         * Unsets the specified edit controller as the active edit controller (release edit lock).\n         * If the specified edit controller is not the active one, an exception will be thrown.\n         * @method deactivate\n         * @param editController {EditController} edit controller releasing the lock\n         */\n        this.deactivate = function(editController) {\n            if (activeEditController !== editController) {\n                throw \"SlickGrid.EditorLock.deactivate: specified editController is not the currently active one\";\n            }\n            activeEditController = null;\n        };\n\n        /***\n         * Attempts to commit the current edit by calling \"commitCurrentEdit\" method on the active edit\n         * controller and returns whether the commit attempt was successful (commit may fail due to validation\n         * errors, etc.).  Edit controller's \"commitCurrentEdit\" must return true if the commit has succeeded\n         * and false otherwise.  If no edit controller is active, returns true.\n         * @method commitCurrentEdit\n         * @return {Boolean}\n         */\n        this.commitCurrentEdit = function() {\n            return (activeEditController ? activeEditController.commitCurrentEdit() : true);\n        };\n\n        /***\n         * Attempts to cancel the current edit by calling \"cancelCurrentEdit\" method on the active edit\n         * controller and returns whether the edit was successfully cancelled.  If no edit controller is\n         * active, returns true.\n         * @method cancelCurrentEdit\n         * @return {Boolean}\n         */\n        this.cancelCurrentEdit = function cancelCurrentEdit() {\n            return (activeEditController ? activeEditController.cancelCurrentEdit() : true);\n        };\n    }\n})(jQuery);\n\n\n"
  },
  {
    "path": "dirigible/shared/static/slickgrid/slick.editors.js",
    "content": "/* THESE FORMATTERS & EDITORS ARE JUST SAMPLES! */\n\n(function($) {\n\n    var SlickEditor = {\n\n        SelectorCellFormatter : function(row, cell, value, columnDef, dataContext) {\n            return (!dataContext ? \"\" : row);\n        },\n\n        PercentCompleteCellFormatter : function(row, cell, value, columnDef, dataContext) {\n            if (value == null || value === \"\")\n                return \"-\";\n            else if (value < 50)\n                return \"<span style='color:red;font-weight:bold;'>\" + value + \"%</span>\";\n            else\n                return \"<span style='color:green'>\" + value + \"%</span>\";\n        },\n\n        GraphicalPercentCompleteCellFormatter : function(row, cell, value, columnDef, dataContext) {\n            if (value == null || value === \"\")\n                return \"\";\n\n            var color;\n\n            if (value < 30)\n                color = \"red\";\n            else if (value < 70)\n                color = \"silver\";\n            else\n                color = \"green\";\n\n            return \"<span class='percent-complete-bar' style='background:\" + color + \";width:\" + value + \"%'></span>\";\n        },\n\n        YesNoCellFormatter : function(row, cell, value, columnDef, dataContext) {\n            return value ? \"Yes\" : \"No\";\n        },\n\n        BoolCellFormatter : function(row, cell, value, columnDef, dataContext) {\n            return value ? \"<img src='../images/tick.png'>\" : \"\";\n        },\n\n        TaskNameFormatter : function(row, cell, value, columnDef, dataContext) {\n            // todo:  html encode\n            var spacer = \"<span style='display:inline-block;height:1px;width:\" + (2 + 15 * dataContext[\"indent\"]) + \"px'></span>\";\n            return spacer + \" <img src='../images/expand.gif'>&nbsp;\" + value;\n        },\n\n        ResourcesFormatter : function(row, cell, value, columnDef, dataContext) {\n            var resources = dataContext[\"resources\"];\n\n            if (!resources || resources.length == 0)\n                return \"\";\n\n            if (columnDef.width < 50)\n                return (resources.length > 1 ? \"<center><img src='../images/user_identity_plus.gif' \" : \"<center><img src='../images/user_identity.gif' \") +\n                        \" title='\" + resources.join(\", \") + \"'></center>\";\n            else\n                return resources.join(\", \");\n        },\n\n        StarFormatter : function(row, cell, value, columnDef, dataContext) {\n            return (value) ? \"<img src='../images/bullet_star.png' align='absmiddle'>\" : \"\";\n        },\n\n\n        TextCellEditor : function(args) {\n            var $input;\n            var defaultValue;\n            var scope = this;\n\n            this.init = function() {\n                $input = $(\"<INPUT type=text class='editor-text' />\")\n                    .appendTo(args.container)\n                    .bind(\"keydown.nav\", function(e) {\n                        if (e.keyCode === $.ui.keyCode.LEFT || e.keyCode === $.ui.keyCode.RIGHT) {\n                            e.stopImmediatePropagation();\n                        }\n                    })\n                    .focus()\n                    .select();\n            };\n\n            this.destroy = function() {\n                $input.remove();\n            };\n\n            this.focus = function() {\n                $input.focus();\n            };\n\n            this.getValue = function() {\n                return $input.val();\n            };\n\n            this.setValue = function(val) {\n                $input.val(val);\n            };\n\n            this.loadValue = function(item) {\n                defaultValue = item[args.column.field] || \"\";\n                $input.val(defaultValue);\n                $input[0].defaultValue = defaultValue;\n                $input.select();\n            };\n\n            this.serializeValue = function() {\n                return $input.val();\n            };\n\n            this.applyValue = function(item,state) {\n                item[args.column.field] = state;\n            };\n\n            this.isValueChanged = function() {\n                return (!($input.val() == \"\" && defaultValue == null)) && ($input.val() != defaultValue);\n            };\n\n            this.validate = function() {\n                if (args.column.validator) {\n                    var validationResults = args.column.validator($input.val());\n                    if (!validationResults.valid)\n                        return validationResults;\n                }\n\n                return {\n                    valid: true,\n                    msg: null\n                };\n            };\n\n            this.init();\n        },\n\n        IntegerCellEditor : function(args) {\n            var $input;\n            var defaultValue;\n            var scope = this;\n\n            this.init = function() {\n                $input = $(\"<INPUT type=text class='editor-text' />\");\n\n                $input.bind(\"keydown.nav\", function(e) {\n                    if (e.keyCode === $.ui.keyCode.LEFT || e.keyCode === $.ui.keyCode.RIGHT) {\n                        e.stopImmediatePropagation();\n                    }\n                });\n\n                $input.appendTo(args.container);\n                $input.focus().select();\n            };\n\n            this.destroy = function() {\n                $input.remove();\n            };\n\n            this.focus = function() {\n                $input.focus();\n            };\n\n            this.loadValue = function(item) {\n                defaultValue = item[args.column.field];\n                $input.val(defaultValue);\n                $input[0].defaultValue = defaultValue;\n                $input.select();\n            };\n\n            this.serializeValue = function() {\n                return parseInt($input.val(),10) || 0;\n            };\n\n            this.applyValue = function(item,state) {\n                item[args.column.field] = state;\n            };\n\n            this.isValueChanged = function() {\n                return (!($input.val() == \"\" && defaultValue == null)) && ($input.val() != defaultValue);\n            };\n\n            this.validate = function() {\n                if (isNaN($input.val()))\n                    return {\n                        valid: false,\n                        msg: \"Please enter a valid integer\"\n                    };\n\n                return {\n                    valid: true,\n                    msg: null\n                };\n            };\n\n            this.init();\n        },\n\n        DateCellEditor : function(args) {\n            var $input;\n            var defaultValue;\n            var scope = this;\n            var calendarOpen = false;\n\n            this.init = function() {\n                $input = $(\"<INPUT type=text class='editor-text' />\");\n                $input.appendTo(args.container);\n                $input.focus().select();\n                $input.datepicker({\n                    showOn: \"button\",\n                    buttonImageOnly: true,\n                    buttonImage: \"../images/calendar.gif\",\n                    beforeShow: function() { calendarOpen = true },\n                    onClose: function() { calendarOpen = false }\n                });\n                $input.width($input.width() - 18);\n            };\n\n            this.destroy = function() {\n                $.datepicker.dpDiv.stop(true,true);\n                $input.datepicker(\"hide\");\n                $input.datepicker(\"destroy\");\n                $input.remove();\n            };\n\n            this.show = function() {\n                if (calendarOpen) {\n                    $.datepicker.dpDiv.stop(true,true).show();\n                }\n            };\n\n            this.hide = function() {\n                if (calendarOpen) {\n                    $.datepicker.dpDiv.stop(true,true).hide();\n                }\n            };\n\n            this.position = function(position) {\n                if (!calendarOpen) return;\n                $.datepicker.dpDiv\n                    .css(\"top\", position.top + 30)\n                    .css(\"left\", position.left);\n            };\n\n            this.focus = function() {\n                $input.focus();\n            };\n\n            this.loadValue = function(item) {\n                defaultValue = item[args.column.field];\n                $input.val(defaultValue);\n                $input[0].defaultValue = defaultValue;\n                $input.select();\n            };\n\n            this.serializeValue = function() {\n                return $input.val();\n            };\n\n            this.applyValue = function(item,state) {\n                item[args.column.field] = state;\n            };\n\n            this.isValueChanged = function() {\n                return (!($input.val() == \"\" && defaultValue == null)) && ($input.val() != defaultValue);\n            };\n\n            this.validate = function() {\n                return {\n                    valid: true,\n                    msg: null\n                };\n            };\n\n            this.init();\n        },\n\n        YesNoSelectCellEditor : function(args) {\n            var $select;\n            var defaultValue;\n            var scope = this;\n\n            this.init = function() {\n                $select = $(\"<SELECT tabIndex='0' class='editor-yesno'><OPTION value='yes'>Yes</OPTION><OPTION value='no'>No</OPTION></SELECT>\");\n                $select.appendTo(args.container);\n                $select.focus();\n            };\n\n            this.destroy = function() {\n                $select.remove();\n            };\n\n            this.focus = function() {\n                $select.focus();\n            };\n\n            this.loadValue = function(item) {\n                $select.val((defaultValue = item[args.column.field]) ? \"yes\" : \"no\");\n                $select.select();\n            };\n\n            this.serializeValue = function() {\n                return ($select.val() == \"yes\");\n            };\n\n            this.applyValue = function(item,state) {\n                item[args.column.field] = state;\n            };\n           \n            this.isValueChanged = function() {\n                return ($select.val() != defaultValue);\n            };\n\n            this.validate = function() {\n                return {\n                    valid: true,\n                    msg: null\n                };\n            };\n\n            this.init();\n        },\n\n        YesNoCheckboxCellEditor : function(args) {\n            var $select;\n            var defaultValue;\n            var scope = this;\n\n            this.init = function() {\n                $select = $(\"<INPUT type=checkbox value='true' class='editor-checkbox' hideFocus>\");\n                $select.appendTo(args.container);\n                $select.focus();\n            };\n\n            this.destroy = function() {\n                $select.remove();\n            };\n\n            this.focus = function() {\n                $select.focus();\n            };\n\n            this.loadValue = function(item) {\n                defaultValue = item[args.column.field];\n                if (defaultValue)\n                    $select.attr(\"checked\", \"checked\");\n                else\n                    $select.removeAttr(\"checked\");\n            };\n\n            this.serializeValue = function() {\n                return $select.attr(\"checked\");\n            };\n\n            this.applyValue = function(item,state) {\n                item[args.column.field] = state;\n            };\n\n            this.isValueChanged = function() {\n                return ($select.attr(\"checked\") != defaultValue);\n            };\n\n            this.validate = function() {\n                return {\n                    valid: true,\n                    msg: null\n                };\n            };\n\n            this.init();\n        },\n\n        PercentCompleteCellEditor : function(args) {\n            var $input, $picker;\n            var defaultValue;\n            var scope = this;\n\n            this.init = function() {\n                $input = $(\"<INPUT type=text class='editor-percentcomplete' />\");\n                $input.width($(args.container).innerWidth() - 25);\n                $input.appendTo(args.container);\n\n                $picker = $(\"<div class='editor-percentcomplete-picker' />\").appendTo(args.container);\n                $picker.append(\"<div class='editor-percentcomplete-helper'><div class='editor-percentcomplete-wrapper'><div class='editor-percentcomplete-slider' /><div class='editor-percentcomplete-buttons' /></div></div>\");\n\n                $picker.find(\".editor-percentcomplete-buttons\").append(\"<button val=0>Not started</button><br/><button val=50>In Progress</button><br/><button val=100>Complete</button>\");\n\n                $input.focus().select();\n\n                $picker.find(\".editor-percentcomplete-slider\").slider({\n                    orientation: \"vertical\",\n                    range: \"min\",\n                    value: defaultValue,\n                    slide: function(event, ui) {\n                        $input.val(ui.value)\n                    }\n                });\n\n                $picker.find(\".editor-percentcomplete-buttons button\").bind(\"click\", function(e) {\n                    $input.val($(this).attr(\"val\"));\n                    $picker.find(\".editor-percentcomplete-slider\").slider(\"value\", $(this).attr(\"val\"));\n                })\n            };\n\n            this.destroy = function() {\n                $input.remove();\n                $picker.remove();\n            };\n\n            this.focus = function() {\n                $input.focus();\n            };\n\n            this.loadValue = function(item) {\n                $input.val(defaultValue = item[args.column.field]);\n                $input.select();\n            };\n\n            this.serializeValue = function() {\n                return parseInt($input.val(),10) || 0;\n            };\n\n            this.applyValue = function(item,state) {\n                item[args.column.field] = state;\n            };\n\n            this.isValueChanged = function() {\n                return (!($input.val() == \"\" && defaultValue == null)) && ((parseInt($input.val(),10) || 0) != defaultValue);\n            };\n\n            this.validate = function() {\n                if (isNaN(parseInt($input.val(),10)))\n                    return {\n                        valid: false,\n                        msg: \"Please enter a valid positive number\"\n                    };\n\n                return {\n                    valid: true,\n                    msg: null\n                };\n            };\n\n            this.init();\n        },\n\n        StarCellEditor : function(args) {\n            var $input;\n            var defaultValue;\n            var scope = this;\n\n            function toggle(e) {\n                if (e.type == \"keydown\" && e.which != 32) return;\n\n                if ($input.css(\"opacity\") == \"1\")\n                    $input.css(\"opacity\", 0.5);\n                else\n                    $input.css(\"opacity\", 1);\n\n                e.preventDefault();\n                e.stopPropagation();\n                return false;\n            }\n\n            this.init = function() {\n                $input = $(\"<IMG src='../images/bullet_star.png' align=absmiddle tabIndex=0 title='Click or press Space to toggle' />\")\n                    .bind(\"click keydown\", toggle)\n                    .appendTo(args.container)\n                    .focus();\n            };\n\n            this.destroy = function() {\n                $input.unbind(\"click keydown\", toggle);\n                $input.remove();\n            };\n\n            this.focus = function() {\n                $input.focus();\n            };\n\n            this.loadValue = function(item) {\n                defaultValue = item[args.column.field];\n                $input.css(\"opacity\", defaultValue ? 1 : 0.2);\n            };\n\n            this.serializeValue = function() {\n                return ($input.css(\"opacity\") == \"1\");\n            };\n\n            this.applyValue = function(item,state) {\n                item[args.column.field] = state;\n            };\n\n            this.isValueChanged = function() {\n                return defaultValue != ($input.css(\"opacity\") == \"1\");\n            };\n\n            this.validate = function() {\n                return {\n                    valid: true,\n                    msg: null\n                };\n            };\n\n            this.init();\n        },\n\n        /*\n         * An example of a \"detached\" editor.\n         * The UI is added onto document BODY and .position(), .show() and .hide() are implemented.\n         * KeyDown events are also handled to provide handling for Tab, Shift-Tab, Esc and Ctrl-Enter.\n         */\n        LongTextCellEditor : function (args) {\n            var $input, $wrapper;\n            var defaultValue;\n            var scope = this;\n\n            this.init = function() {\n                var $container = $(\"body\");\n\n                $wrapper = $(\"<DIV style='z-index:10000;position:absolute;background:white;padding:5px;border:3px solid gray; -moz-border-radius:10px; border-radius:10px;'/>\")\n                    .appendTo($container);\n\n                $input = $(\"<TEXTAREA hidefocus rows=5 style='backround:white;width:250px;height:80px;border:0;outline:0'>\")\n                    .appendTo($wrapper);\n\n                $(\"<DIV style='text-align:right'><BUTTON>Save</BUTTON><BUTTON>Cancel</BUTTON></DIV>\")\n                    .appendTo($wrapper);\n\n                $wrapper.find(\"button:first\").bind(\"click\", this.save);\n                $wrapper.find(\"button:last\").bind(\"click\", this.cancel);\n                $input.bind(\"keydown\", this.handleKeyDown);\n\n                scope.position(args.position);\n                $input.focus().select();\n            };\n\n            this.handleKeyDown = function(e) {\n                if (e.which == $.ui.keyCode.ENTER && e.ctrlKey) {\n                    scope.save();\n                }\n                else if (e.which == $.ui.keyCode.ESCAPE) {\n                    e.preventDefault();\n                    scope.cancel();\n                }\n                else if (e.which == $.ui.keyCode.TAB && e.shiftKey) {\n                    e.preventDefault();\n                    grid.navigatePrev();\n                }\n                else if (e.which == $.ui.keyCode.TAB) {\n                    e.preventDefault();\n                    grid.navigateNext();\n                }\n            };\n\n            this.save = function() {\n                args.commitChanges();\n            };\n\n            this.cancel = function() {\n                $input.val(defaultValue);\n                args.cancelChanges();\n            };\n\n            this.hide = function() {\n                $wrapper.hide();\n            };\n\n            this.show = function() {\n                $wrapper.show();\n            };\n\n            this.position = function(position) {\n                $wrapper\n                    .css(\"top\", position.top - 5)\n                    .css(\"left\", position.left - 5)\n            };\n\n            this.destroy = function() {\n                $wrapper.remove();\n            };\n\n            this.focus = function() {\n                $input.focus();\n            };\n\n            this.loadValue = function(item) {\n                $input.val(defaultValue = item[args.column.field]);\n                $input.select();\n            };\n\n            this.serializeValue = function() {\n                return $input.val();\n            };\n\n            this.applyValue = function(item,state) {\n                item[args.column.field] = state;\n            };\n\n            this.isValueChanged = function() {\n                return (!($input.val() == \"\" && defaultValue == null)) && ($input.val() != defaultValue);\n            };\n\n            this.validate = function() {\n                return {\n                    valid: true,\n                    msg: null\n                };\n            };\n\n            this.init();\n        }\n\n    };\n\n    $.extend(window, SlickEditor);\n\n})(jQuery);\n"
  },
  {
    "path": "dirigible/shared/static/slickgrid/slick.grid.css",
    "content": "/*\nIMPORTANT:\nIn order to preserve the uniform grid appearance, all cell styles need to have padding, margin and border sizes.\nNo built-in (selected, editable, highlight, flashing, invalid, loading, :focus) or user-specified CSS\nclasses should alter those!\n*/\n\n\n.slick-header.ui-state-default, .slick-headerrow.ui-state-default {\n\twidth: 100%;\n\toverflow: hidden;\n\tborder-left: 0px;\n}\n\n.slick-header-columns, .slick-headerrow-columns {\n\twidth: 999999px;\n\tposition: relative;\n\twhite-space: nowrap;\n\tcursor: default;\n\toverflow: hidden;\n}\n\n.slick-header-column.ui-state-default {\n\tposition: relative;\n\tdisplay: inline-block;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\theight: 16px;\n\tline-height: 16px;\n\tmargin: 0;\n\tpadding: 4px;\n\tborder-right: 1px solid silver;\n\tborder-left: 0px;\n\tborder-top: 0px;\n\tborder-bottom: 0px;\n\tfloat: left;\n}\n\n.slick-headerrow-column.ui-state-default {\n    padding: 4px;\n}\n\n.slick-header-column-sorted {\n\tfont-style: italic;\n}\n\n.slick-sort-indicator {\n\tdisplay: inline-block;\n\twidth: 8px;\n\theight: 5px;\n\tmargin-left: 4px;\n}\n\n.slick-sort-indicator-desc {\n\tbackground: url(images/sort-desc.gif);\n}\n\n.slick-sort-indicator-asc {\n\tbackground: url(images/sort-asc.gif);\n}\n\n.slick-resizable-handle {\n\tposition: absolute;\n\tfont-size: 0.1px;\n\tdisplay: block;\n\tcursor: col-resize;\n\twidth: 4px;\n\tright: 0px;\n\ttop: 0;\n\theight: 100%;\n}\n\n.slick-sortable-placeholder {\n\tbackground: silver;\n}\n\n.grid-canvas {\n\tposition: relative;\n\toutline: 0;\n}\n\n.slick-row.ui-widget-content, .slick-row.ui-state-active {\n\tposition: absolute;\n\tborder: 0px;\n}\n\n.slick-cell, .slick-headerrow-column {\n\tfloat: left;\n\n\tborder: 1px solid transparent;\n\tborder-right: 1px dotted silver;\n\tborder-bottom-color: silver;\n\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n\tvertical-align: middle;\n\tz-index: 1;\n\tpadding: 1px 2px 2px 1px;\n    margin: 0;\n    \n\twhite-space: nowrap;\n\n\tcursor: default;\n}\n\n.slick-group {\n    position: absolute;\n    top: 0;\n    bottom: 0;\n    left: 0;\n    right: 0;\n}\n\n.slick-group-toggle {\n    display: inline-block;\n}\n\n.slick-cell.highlighted {\n    background: lightskyblue;\n    background: rgba(0,0,255,0.2);\n    -webkit-transition: all 0.5s;\n    -moz-transition: all 0.5s;\n    transition: all 0.5s;\n}\n\n.slick-cell.flashing {\n    border: 1px solid red !important;\n}\n\n.slick-cell.editable {\n\tz-index: 11;\n\toverflow: visible;\n\tbackground: white;\n\tborder-color: black;\n\tborder-style: solid;\n}\n\n.slick-cell:focus {\n\toutline: none;\n}\n\n.slick-reorder-proxy {\n\tdisplay: inline-block;\n\tbackground: blue;\n\topacity: 0.15;\n\tfilter: alpha(opacity=15);\n\tcursor: move;\n}\n\n.slick-reorder-guide {\n\tdisplay: inline-block;\n\theight: 2px;\n\tbackground: blue;\n\topacity: 0.7;\n\tfilter: alpha(opacity=70);\n}\n\n.slick-selection {\n    z-index: 10;\n    position: absolute;\n    border: 2px dashed black;\n}"
  },
  {
    "path": "dirigible/shared/static/slickgrid/slick.grid.js",
    "content": "/**\n * @license\n * (c) 2009-2010 Michael Leibman\n * michael{dot}leibman{at}gmail{dot}com\n * http://github.com/mleibman/slickgrid\n * Distributed under MIT license.\n * All rights reserved.\n *\n * SlickGrid v2.0 alpha\n *\n * NOTES:\n *     Cell/row DOM manipulations are done directly bypassing jQuery's DOM manipulation methods.\n *     This increases the speed dramatically, but can only be done safely because there are no event handlers\n *     or data associated with any cell/row DOM nodes.  Cell editors must make sure they implement .destroy()\n *     and do proper cleanup.\n */\n\n// make sure required JavaScript modules are loaded\nif (typeof jQuery === \"undefined\") {\n    throw \"SlickGrid requires jquery module to be loaded\";\n}\nif (!jQuery.fn.drag) {\n    throw \"SlickGrid requires jquery.event.drag module to be loaded\";\n}\nif (typeof Slick === \"undefined\") {\n    throw \"slick.core.js not loaded\";\n}\n\n\n(function($) {\n    // Slick.Grid\n    $.extend(true, window, {\n        Slick: {\n            Grid: SlickGrid\n        }\n    });\n\n    var scrollbarDimensions; // shared across all grids on this page\n\n    //////////////////////////////////////////////////////////////////////////////////////////////\n    // SlickGrid class implementation (available as Slick.Grid)\n\n    /**\n     * @param {Node}              container   Container node to create the grid in.\n     * @param {Array,Object}      data        An array of objects for databinding.\n     * @param {Array}             columns     An array of column definitions.\n     * @param {Object}            options     Grid options.\n     **/\n    function SlickGrid(container,data,columns,options) {\n        /// <summary>\n        /// Create and manage virtual grid in the specified $container,\n        /// connecting it to the specified data source. Data is presented\n        /// as a grid with the specified columns and data.length rows.\n        /// Options alter behaviour of the grid.\n        /// </summary>\n\n        // settings\n        var defaults = {\n            headerHeight: 25,\n            rowHeight: 25,\n            defaultColumnWidth: 80,\n            enableAddRow: false,\n            leaveSpaceForNewRows: false,\n            editable: false,\n            autoEdit: true,\n            alwaysNavigateDownOnCommit: false,\n            enableCellNavigation: true,\n            shiftKeyPreventsNavigation: true,\n            enableCellRangeSelection: false,\n            enableColumnReorder: true,\n            asyncEditorLoading: false,\n            asyncEditorLoadDelay: 100,\n            forceFitColumns: false,\n            enableAsyncPostRender: false,\n            asyncPostRenderDelay: 60,\n            autoHeight: false,\n            editorLock: Slick.GlobalEditorLock,\n            showHeaderRow: false,\n            headerRowHeight: 25,\n            showTopPanel: false,\n            topPanelHeight: 25,\n            syncColumnCellResize: false,\n            formatterFactory: null,\n            editorFactory: null,\n            cellFlashingCssClass: \"flashing\",\n            selectedCellCssClass: \"selected\",\n            multiSelect: true\n        },\n        gridData;\n\n        var columnDefaults = {\n            name: \"\",\n            resizable: true,\n            sortable: false,\n            minWidth: 30,\n            rerenderOnResize: false,\n            unselectable: false,\n            groupTotalsFormatter: null,\n            headerCssClass: null\n        };\n\n        // scroller\n        var maxSupportedCssHeight;      // browser's breaking point\n        var th;                         // virtual height\n        var h;                          // real scrollable height\n        var ph;                         // page height\n        var n;                          // number of pages\n        var cj;                         // \"jumpiness\" coefficient\n\n        var page = 0;                   // current page\n        var offset = 0;                 // current page offset\n        var scrollDir = 1;\n\n        // private\n        var $container;\n        var uid = \"slickgrid_\" + Math.round(1000000 * Math.random());\n        var self = this;\n        var $headerScroller;\n        var $headers;\n        var $headerRow, $headerRowScroller;\n        var $topPanelScroller;\n        var $topPanel;\n        var $viewport;\n        var $canvas;\n        var $style;\n        var stylesheet;\n        var viewportH, viewportW;\n        var viewportHasHScroll;\n        var headerColumnWidthDiff, headerColumnHeightDiff, cellWidthDiff, cellHeightDiff;  // padding+border\n        var absoluteColumnMinWidth;\n\n        var activeRow, activeCell;\n        var activeCellNode = null;\n        var currentEditor = null;\n        var serializedEditorValue;\n        var editController;\n\n        var rowsCache = {};\n        var renderedRows = 0;\n        var numVisibleRows;\n        var prevScrollTop = 0;\n        var scrollTop = 0;\n        var lastRenderedScrollTop = 0;\n        var prevScrollLeft = 0;\n        var avgRowRenderTime = 10;\n\n        var selectionModel;\n        var selectedRows = [];\n\n        var plugins = [];\n        var cellCssClasses = {};\n\n        var columnsById = {};\n        var sortColumnId;\n        var sortAsc = true;\n\n        // async call handles\n        var h_editorLoader = null;\n        var h_render = null;\n        var h_postrender = null;\n        var postProcessedRows = {};\n        var postProcessToRow = null;\n        var postProcessFromRow = null;\n\n        // perf counters\n        var counter_rows_rendered = 0;\n        var counter_rows_removed = 0;\n\n\n        //////////////////////////////////////////////////////////////////////////////////////////////\n        // Initialization\n\n        function init() {\n            /// <summary>\n            /// Initialize 'this' (self) instance of a SlickGrid.\n            /// This function is called by the constructor.\n            /// </summary>\n\n            $container = $(container);\n\n            gridData = data;\n\n            maxSupportedCssHeight = getMaxSupportedCssHeight();\n\n            scrollbarDimensions = scrollbarDimensions || measureScrollbar(); // skip measurement if already have dimensions\n            options = $.extend({},defaults,options);\n            columnDefaults.width = options.defaultColumnWidth;\n\n            // validate loaded JavaScript modules against requested options\n            if (options.enableColumnReorder && !$.fn.sortable) {\n                throw new Error(\"SlickGrid's \\\"enableColumnReorder = true\\\" option requires jquery-ui.sortable module to be loaded\");\n            }\n\n            editController = {\n                \"commitCurrentEdit\": commitCurrentEdit,\n                \"cancelCurrentEdit\": cancelCurrentEdit\n            };\n\n            $container\n                .empty()\n                .attr(\"tabIndex\",0)\n                .attr(\"hideFocus\",true)\n                .css(\"overflow\",\"hidden\")\n                .css(\"outline\",0)\n                .addClass(uid)\n                .addClass(\"ui-widget\");\n\n            // set up a positioning container if needed\n            if (!/relative|absolute|fixed/.test($container.css(\"position\")))\n                $container.css(\"position\",\"relative\");\n\n            $headerScroller = $(\"<div class='slick-header ui-state-default' style='overflow:hidden;position:relative;' />\").appendTo($container);\n            $headers = $(\"<div class='slick-header-columns' style='width:10000px; left:-1000px' />\").appendTo($headerScroller);\n\n            $headerRowScroller = $(\"<div class='slick-headerrow ui-state-default' style='overflow:hidden;position:relative;' />\").appendTo($container);\n            $headerRow = $(\"<div class='slick-headerrow-columns' style='width:10000px;' />\").appendTo($headerRowScroller);\n\n            $topPanelScroller = $(\"<div class='slick-top-panel-scroller ui-state-default' style='overflow:hidden;position:relative;' />\").appendTo($container);\n            $topPanel = $(\"<div class='slick-top-panel' style='width:10000px' />\").appendTo($topPanelScroller);\n\n            if (!options.showTopPanel) {\n                $topPanelScroller.hide();\n            }\n\n            if (!options.showHeaderRow) {\n                $headerRowScroller.hide();\n            }\n\n            $viewport = $(\"<div class='slick-viewport' tabIndex='0' hideFocus style='width:100%;overflow-x:auto;outline:0;position:relative;overflow-y:auto;'>\").appendTo($container);\n            $canvas = $(\"<div class='grid-canvas' tabIndex='0' hideFocus />\").appendTo($viewport);\n\n            // header columns and cells may have different padding/border skewing width calculations (box-sizing, hello?)\n            // calculate the diff so we can set consistent sizes\n            measureCellPaddingAndBorder();\n\n            // for usability reasons, all text selection in SlickGrid is disabled\n            // with the exception of input and textarea elements (selection must\n            // be enabled there so that editors work as expected); note that\n            // selection in grid cells (grid body) is already unavailable in\n            // all browsers except IE\n            disableSelection($headers); // disable all text selection in header (including input and textarea)\n            $viewport.bind(\"selectstart.ui\", function (event) { return $(event.target).is(\"input,textarea\"); }); // disable text selection in grid cells except in input and textarea elements (this is IE-specific, because selectstart event will only fire in IE)\n\n            viewportW = parseFloat($.css($container[0], \"width\", true));\n\n            createColumnHeaders();\n            setupColumnSort();\n            createCssRules();\n            resizeAndRender();\n\n            bindAncestorScrollEvents();\n            $viewport.bind(\"scroll.slickgrid\", handleScroll);\n            $container.bind(\"resize.slickgrid\", resizeAndRender);\n            $headerScroller\n                .bind(\"contextmenu.slickgrid\", handleHeaderContextMenu)\n                .bind(\"click.slickgrid\", handleHeaderClick);\n\n            $canvas\n                .bind(\"keydown.slickgrid\", handleKeyDown)\n                .bind(\"click.slickgrid\", handleClick)\n                .bind(\"dblclick.slickgrid\", handleDblClick)\n                .bind(\"contextmenu.slickgrid\", handleContextMenu)\n                .bind(\"draginit\", handleDragInit)\n                .bind(\"dragstart\", handleDragStart)\n                .bind(\"drag\", handleDrag)\n                .bind(\"dragend\", handleDragEnd);\n\n            $canvas.delegate(\".slick-cell\", \"mouseenter\", handleMouseEnter);\n            $canvas.delegate(\".slick-cell\", \"mouseleave\", handleMouseLeave);\n        }\n\n        function registerPlugin(plugin) {\n            plugins.unshift(plugin);\n            plugin.init(self);\n        }\n\n        function unregisterPlugin(plugin) {\n            for (var i = plugins.length; i >= 0; i--) {\n                if (plugins[i] === plugin) {\n                    if (plugins[i].destroy) {\n                        plugins[i].destroy();\n                    }\n                    plugins.splice(i, 1);\n                    break;\n                }\n            }\n        }\n\n        function setSelectionModel(model) {\n            if (selectionModel) {\n                selectionModel.onSelectedRangesChanged.unsubscribe(handleSelectedRangesChanged);\n                if (selectionModel.destroy) {\n                    selectionModel.destroy();\n                }\n            }\n\n            selectionModel = model;\n            selectionModel.init(self);\n\n            selectionModel.onSelectedRangesChanged.subscribe(handleSelectedRangesChanged);\n        }\n\n        function getSelectionModel() {\n            return selectionModel;\n        }\n\n        function getCanvasNode() {\n            return $canvas[0];\n        }\n\n        function measureScrollbar() {\n            /// <summary>\n            /// Measure width of a vertical scrollbar\n            /// and height of a horizontal scrollbar.\n            /// </summary\n            /// <returns>\n            /// { width: pixelWidth, height: pixelHeight }\n            /// </returns>\n            var $c = $(\"<div style='position:absolute; top:-10000px; left:-10000px; width:100px; height:100px; overflow:scroll;'></div>\").appendTo(\"body\");\n            var dim = { width: $c.width() - $c[0].clientWidth, height: $c.height() - $c[0].clientHeight };\n            $c.remove();\n            return dim;\n        }\n\n        function getRowWidth() {\n            var rowWidth = 0;\n            var i = columns.length;\n            while (i--) {\n                rowWidth += (columns[i].currentWidth || columns[i].width || columnDefaults.width);\n            }\n            return rowWidth;\n        }\n\n        function setCanvasWidth(width) {\n            $canvas.width(width);\n            viewportHasHScroll = (width > viewportW - scrollbarDimensions.width);\n        }\n\n        function disableSelection($target) {\n            /// <summary>\n            /// Disable text selection (using mouse) in\n            /// the specified target.\n            /// </summary\n            if ($target && $target.jquery) {\n                $target.attr('unselectable', 'on').css('MozUserSelect', 'none').bind('selectstart.ui', function() { return false; }); // from jquery:ui.core.js 1.7.2\n            }\n        }\n\n        function getMaxSupportedCssHeight() {\n            var increment = 1000000;\n            var supportedHeight = 0;\n            // FF reports the height back but still renders blank after ~6M px\n            var testUpTo = ($.browser.mozilla) ? 5000000 : 1000000000;\n            var div = $(\"<div style='display:none' />\").appendTo(document.body);\n\n            while (supportedHeight <= testUpTo) {\n                div.css(\"height\", supportedHeight + increment);\n                if (div.height() !== supportedHeight + increment)\n                    break;\n                else\n                    supportedHeight += increment;\n            }\n\n            div.remove();\n            return supportedHeight;\n        }\n\n        // TODO:  this is static.  need to handle page mutation.\n        function bindAncestorScrollEvents() {\n            var elem = $canvas[0];\n            while ((elem = elem.parentNode) != document.body) {\n                // bind to scroll containers only\n                if (elem == $viewport[0] || elem.scrollWidth != elem.clientWidth || elem.scrollHeight != elem.clientHeight)\n                    $(elem).bind(\"scroll.slickgrid\", handleActiveCellPositionChange);\n            }\n        }\n\n        function unbindAncestorScrollEvents() {\n            $canvas.parents().unbind(\"scroll.slickgrid\");\n        }\n\n        function updateColumnHeader(columnId, title, toolTip) {\n            var idx = getColumnIndex(columnId);\n            var $header = $headers.children().eq(idx);\n            if ($header) {\n                columns[idx].name = title;\n                columns[idx].toolTip = toolTip;\n                $header\n                    .attr(\"title\", toolTip || title || \"\")\n                    .children().eq(0).html(title);\n            }\n        }\n\n        function getHeaderRow() {\n            return $headerRow[0];\n        }\n\n        function getHeaderRowColumn(columnId) {\n            var idx = getColumnIndex(columnId);\n            var $header = $headerRow.children().eq(idx);\n            return $header && $header[0];\n        }\n\n\n        function createColumnHeaders() {\n            var i;\n\n            function hoverBegin() {\n                $(this).addClass(\"ui-state-hover\");\n            }\n            function hoverEnd() {\n                $(this).removeClass(\"ui-state-hover\");\n            }\n\n            $headers.empty();\n            $headerRow.empty();\n            columnsById = {};\n\n            for (i = 0; i < columns.length; i++) {\n                var m = columns[i] = $.extend({},columnDefaults,columns[i]);\n                columnsById[m.id] = i;\n\n                var header = $(\"<div class='ui-state-default slick-header-column' id='\" + uid + m.id + \"' />\")\n                    .html(\"<span class='slick-column-name'>\" + m.name + \"</span>\")\n                    .width((m.currentWidth || m.width) - headerColumnWidthDiff)\n                    .attr(\"title\", m.toolTip || m.name || \"\")\n                    .data(\"fieldId\", m.id)\n                    .addClass(m.headerCssClass || \"\")\n                    .appendTo($headers);\n\n                if (options.enableColumnReorder || m.sortable) {\n                    header.hover(hoverBegin, hoverEnd);\n                }\n\n                if (m.sortable) {\n                    header.append(\"<span class='slick-sort-indicator' />\");\n                }\n\n                if (options.showHeaderRow) {\n                    $(\"<div class='ui-state-default slick-headerrow-column c\" + i + \"'></div>\").appendTo($headerRow);\n                }\n            }\n\n            setSortColumn(sortColumnId,sortAsc);\n            setupColumnResize();\n            if (options.enableColumnReorder) {\n                setupColumnReorder();\n            }\n        }\n\n        function setupColumnSort() {\n            $headers.click(function(e) {\n                if ($(e.target).hasClass(\"slick-resizable-handle\")) {\n                    return;\n                }\n\n                var $col = $(e.target).closest(\".slick-header-column\");\n                if (!$col.length)\n                    return;\n\n                var column = columns[getSiblingIndex($col[0])];\n                if (column.sortable) {\n                    if (!getEditorLock().commitCurrentEdit())\n                        return;\n\n                    if (column.id === sortColumnId) {\n                        sortAsc = !sortAsc;\n                    }\n                    else {\n                        sortColumnId = column.id;\n                        sortAsc = true;\n                    }\n\n                    setSortColumn(sortColumnId,sortAsc);\n                    trigger(self.onSort, {sortCol:column,sortAsc:sortAsc});\n                }\n            });\n        }\n\n        function setupColumnReorder() {\n            $headers.sortable({\n                containment: \"parent\",\n                axis: \"x\",\n                cursor: \"default\",\n                tolerance: \"intersection\",\n                helper: \"clone\",\n                placeholder: \"slick-sortable-placeholder ui-state-default slick-header-column\",\n                forcePlaceholderSize: true,\n                start: function(e, ui) { $(ui.helper).addClass(\"slick-header-column-active\"); },\n                beforeStop: function(e, ui) { $(ui.helper).removeClass(\"slick-header-column-active\"); },\n                stop: function(e) {\n                    if (!getEditorLock().commitCurrentEdit()) {\n                        $(this).sortable(\"cancel\");\n                        return;\n                    }\n\n                    var reorderedIds = $headers.sortable(\"toArray\");\n                    var reorderedColumns = [];\n                    for (var i=0; i<reorderedIds.length; i++) {\n                        reorderedColumns.push(columns[getColumnIndex(reorderedIds[i].replace(uid,\"\"))]);\n                    }\n                    setColumns(reorderedColumns);\n\n                    trigger(self.onColumnsReordered, {});\n                    e.stopPropagation();\n                    setupColumnResize();\n                }\n            });\n        }\n\n        function setupColumnResize() {\n            var $col, j, c, pageX, columnElements, minPageX, maxPageX, firstResizable, lastResizable, originalCanvasWidth;\n            columnElements = $headers.children();\n            columnElements.find(\".slick-resizable-handle\").remove();\n            columnElements.each(function(i,e) {\n                if (columns[i].resizable) {\n                    if (firstResizable === undefined) { firstResizable = i; }\n                    lastResizable = i;\n                }\n            });\n            if (firstResizable === undefined) {\n                return;\n            }\n            columnElements.each(function(i,e) {\n                if (i < firstResizable || (options.forceFitColumns && i >= lastResizable)) { return; }\n                $col = $(e);\n                $(\"<div class='slick-resizable-handle' />\")\n                    .appendTo(e)\n                    .bind(\"dragstart\", function(e,dd) {\n                        if (!getEditorLock().commitCurrentEdit()) { return false; }\n                        pageX = e.pageX;\n                        $(this).parent().addClass(\"slick-header-column-active\");\n                        var shrinkLeewayOnRight = null, stretchLeewayOnRight = null;\n                        // lock each column's width option to current width\n                        columnElements.each(function(i,e) { columns[i].previousWidth = $(e).outerWidth(); });\n                        if (options.forceFitColumns) {\n                            shrinkLeewayOnRight = 0;\n                            stretchLeewayOnRight = 0;\n                            // colums on right affect maxPageX/minPageX\n                            for (j = i + 1; j < columnElements.length; j++) {\n                                c = columns[j];\n                                if (c.resizable) {\n                                    if (stretchLeewayOnRight !== null) {\n                                        if (c.maxWidth) {\n                                            stretchLeewayOnRight += c.maxWidth - c.previousWidth;\n                                        }\n                                        else {\n                                            stretchLeewayOnRight = null;\n                                        }\n                                    }\n                                    shrinkLeewayOnRight += c.previousWidth - Math.max(c.minWidth || 0, absoluteColumnMinWidth);\n                                }\n                            }\n                        }\n                        var shrinkLeewayOnLeft = 0, stretchLeewayOnLeft = 0;\n                        for (j = 0; j <= i; j++) {\n                            // columns on left only affect minPageX\n                            c = columns[j];\n                            if (c.resizable) {\n                                if (stretchLeewayOnLeft !== null) {\n                                    if (c.maxWidth) {\n                                        stretchLeewayOnLeft += c.maxWidth - c.previousWidth;\n                                    }\n                                    else {\n                                        stretchLeewayOnLeft = null;\n                                    }\n                                }\n                                shrinkLeewayOnLeft += c.previousWidth - Math.max(c.minWidth || 0, absoluteColumnMinWidth);\n                            }\n                        }\n                        if (shrinkLeewayOnRight === null) { shrinkLeewayOnRight = 100000; }\n                        if (shrinkLeewayOnLeft === null) { shrinkLeewayOnLeft = 100000; }\n                        if (stretchLeewayOnRight === null) { stretchLeewayOnRight = 100000; }\n                        if (stretchLeewayOnLeft === null) { stretchLeewayOnLeft = 100000; }\n                        maxPageX = pageX + Math.min(shrinkLeewayOnRight, stretchLeewayOnLeft);\n                        minPageX = pageX - Math.min(shrinkLeewayOnLeft, stretchLeewayOnRight);\n                        originalCanvasWidth = $canvas.width();\n                    })\n                    .bind(\"drag\", function(e,dd) {\n                        var actualMinWidth, d = Math.min(maxPageX, Math.max(minPageX, e.pageX)) - pageX, x, ci;\n                        if (d < 0) { // shrink column\n                            x = d;\n                            for (j = i; j >= 0; j--) {\n                                c = columns[j];\n                                if (c.resizable) {\n                                    actualMinWidth = Math.max(c.minWidth || 0, absoluteColumnMinWidth);\n                                    if (x && c.previousWidth + x < actualMinWidth) {\n                                        x += c.previousWidth - actualMinWidth;\n                                        styleColumnWidth(j, actualMinWidth, options.syncColumnCellResize);\n                                    } else {\n                                        styleColumnWidth(j, c.previousWidth + x, options.syncColumnCellResize);\n                                        x = 0;\n                                    }\n                                }\n                            }\n\n                            if (options.forceFitColumns) {\n                                x = -d;\n                                for (j = i + 1; j < columnElements.length; j++) {\n                                    c = columns[j];\n                                    if (c.resizable) {\n                                        if (x && c.maxWidth && (c.maxWidth - c.previousWidth < x)) {\n                                            x -= c.maxWidth - c.previousWidth;\n                                            styleColumnWidth(j, c.maxWidth, options.syncColumnCellResize);\n                                        } else {\n                                            styleColumnWidth(j, c.previousWidth + x, options.syncColumnCellResize);\n                                            x = 0;\n                                        }\n                                    }\n                                }\n                            } else if (options.syncColumnCellResize) {\n                                setCanvasWidth(originalCanvasWidth + d);\n                            }\n                        } else { // stretch column\n                            x = d;\n                            for (j = i; j >= 0; j--) {\n                                c = columns[j];\n                                if (c.resizable) {\n                                    if (x && c.maxWidth && (c.maxWidth - c.previousWidth < x)) {\n                                        x -= c.maxWidth - c.previousWidth;\n                                        styleColumnWidth(j, c.maxWidth, options.syncColumnCellResize);\n                                    } else {\n                                        styleColumnWidth(j, c.previousWidth + x, options.syncColumnCellResize);\n                                        x = 0;\n                                    }\n                                }\n                            }\n\n                            if (options.forceFitColumns) {\n                                x = -d;\n                                for (j = i + 1; j < columnElements.length; j++) {\n                                    c = columns[j];\n                                    if (c.resizable) {\n                                        actualMinWidth = Math.max(c.minWidth || 0, absoluteColumnMinWidth);\n                                        if (x && c.previousWidth + x < actualMinWidth) {\n                                            x += c.previousWidth - actualMinWidth;\n                                            styleColumnWidth(j, actualMinWidth, options.syncColumnCellResize);\n                                        } else {\n                                            styleColumnWidth(j, c.previousWidth + x, options.syncColumnCellResize);\n                                            x = 0;\n                                        }\n                                    }\n                                }\n                            } else if (options.syncColumnCellResize) {\n                                setCanvasWidth(originalCanvasWidth + d);\n                            }\n                        }\n                    })\n                    .bind(\"dragend\", function(e,dd) {\n                        var newWidth;\n                        $(this).parent().removeClass(\"slick-header-column-active\");\n                        for (j = 0; j < columnElements.length; j++) {\n                            c = columns[j];\n                            newWidth = $(columnElements[j]).outerWidth();\n\n                            if (c.previousWidth !== newWidth && c.rerenderOnResize) {\n                                invalidateAllRows();\n                            }\n                            if (options.forceFitColumns) {\n                                c.width = Math.floor(c.width * (newWidth - c.previousWidth) / c.previousWidth) + c.width;\n                            } else {\n                                c.width = newWidth;\n                            }\n                            if (!options.syncColumnCellResize && c.previousWidth !== newWidth) {\n                                styleColumnWidth(j, newWidth, true);\n                            }\n                        }\n                        resizeCanvas();\n                        trigger(self.onColumnsResized, {});\n                    });\n                });\n        }\n\n        function getVBoxDelta($el) {\n            var p = [\"borderTopWidth\", \"borderBottomWidth\", \"paddingTop\", \"paddingBottom\"];\n            var delta = 0;\n            $.each(p, function(n,val) { delta += parseFloat($el.css(val)) || 0; });\n            return delta;\n        }\n\n        function measureCellPaddingAndBorder() {\n            var el;\n            var h = [\"borderLeftWidth\", \"borderRightWidth\", \"paddingLeft\", \"paddingRight\"];\n            var v = [\"borderTopWidth\", \"borderBottomWidth\", \"paddingTop\", \"paddingBottom\"];\n\n            el = $(\"<div class='ui-state-default slick-header-column' style='visibility:hidden'>-</div>\").appendTo($headers);\n            headerColumnWidthDiff = headerColumnHeightDiff = 0;\n            $.each(h, function(n,val) { headerColumnWidthDiff += parseFloat(el.css(val)) || 0; });\n            $.each(v, function(n,val) { headerColumnHeightDiff += parseFloat(el.css(val)) || 0; });\n            el.remove();\n\n            var r = $(\"<div class='slick-row' />\").appendTo($canvas);\n            el = $(\"<div class='slick-cell' id='' style='visibility:hidden'>-</div>\").appendTo(r);\n            cellWidthDiff = cellHeightDiff = 0;\n            $.each(h, function(n,val) { cellWidthDiff += parseFloat(el.css(val)) || 0; });\n            $.each(v, function(n,val) { cellHeightDiff += parseFloat(el.css(val)) || 0; });\n            r.remove();\n\n            absoluteColumnMinWidth = Math.max(headerColumnWidthDiff,cellWidthDiff);\n        }\n\n        function createCssRules() {\n            $style = $(\"<style type='text/css' rel='stylesheet' />\").appendTo($(\"head\"));\n            var rowHeight = (options.rowHeight - cellHeightDiff);\n\n            var rules = [\n                \".\" + uid + \" .slick-header-column { left: 1000px; }\",\n                \".\" + uid + \" .slick-top-panel { height:\" + options.topPanelHeight + \"px; }\",\n                \".\" + uid + \" .slick-headerrow-columns { height:\" + options.headerRowHeight + \"px; }\",\n                \".\" + uid + \" .slick-cell { height:\" + rowHeight + \"px; }\",\n                \".\" + uid + \" .slick-row { width:\" + getRowWidth() + \"px; }\"\n            ];\n\n            for (var i=0; i<columns.length; i++) {\n                rules.push(\n                    \".\" + uid + \" .c\" + i + \" { \" +\n                    \"width:\" + ((columns[i].currentWidth || columns[i].width) - cellWidthDiff) + \"px; \" +\n                    \" } \");\n            }\n\n            if ($style[0].styleSheet) { // IE\n                $style[0].styleSheet.cssText = rules.join(\"\");\n            }\n            else {\n                $style[0].appendChild(document.createTextNode(rules.join(\" \")));\n            }\n\n            var sheets = document.styleSheets;\n            for (var i=0; i<sheets.length; i++) {\n                if ((sheets[i].ownerNode || sheets[i].owningElement) == $style[0]) {\n                    stylesheet = sheets[i];\n                    break;\n                }\n            }\n        }\n\n        function findCssRule(selector) {\n            var rules = (stylesheet.cssRules || stylesheet.rules);\n\n            for (var i=0; i<rules.length; i++) {\n                if (rules[i].selectorText == selector)\n                    return rules[i];\n            }\n\n            return null;\n        }\n\n        function findCssRuleForCell(index) {\n            return findCssRule(\".\" + uid + \" .c\" + index);\n        }\n\n        function removeCssRules() {\n            $style.remove();\n        }\n\n        function destroy() {\n            getEditorLock().cancelCurrentEdit();\n\n            trigger(self.onBeforeDestroy, {});\n\n            for (var i = 0; i < plugins.length; i++) {\n                unregisterPlugin(plugin);\n            }\n\n            if (options.enableColumnReorder && $headers.sortable)\n                $headers.sortable(\"destroy\");\n\n            unbindAncestorScrollEvents();\n            $container.unbind(\".slickgrid\");\n            removeCssRules();\n\n            $canvas.unbind(\"draginit dragstart dragend drag\");\n            $container.empty().removeClass(uid);\n        }\n\n\n        //////////////////////////////////////////////////////////////////////////////////////////////\n        // General\n\n        function trigger(evt, args, e) {\n            e = e || new Slick.EventData();\n            args = args || {};\n            args.grid = self;\n            return evt.notify(args, e, self);\n        }\n\n        function getEditorLock() {\n            return options.editorLock;\n        }\n\n        function getEditController() {\n            return editController;\n        }\n\n        function getColumnIndex(id) {\n            return columnsById[id];\n        }\n\n        function autosizeColumns() {\n            var i, c,\n                widths = [],\n                shrinkLeeway = 0,\n                availWidth = (options.autoHeight ? viewportW : viewportW - scrollbarDimensions.width), // with AutoHeight, we do not need to accomodate the vertical scroll bar\n                total = 0,\n                existingTotal = 0;\n\n            for (i = 0; i < columns.length; i++) {\n                c = columns[i];\n                widths.push(c.width);\n                existingTotal += c.width;\n                shrinkLeeway += c.width - Math.max(c.minWidth || 0, absoluteColumnMinWidth);\n            }\n\n            total = existingTotal;\n\n            invalidateAllRows();\n\n            // shrink\n            while (total > availWidth) {\n                if (!shrinkLeeway) { return; }\n                var shrinkProportion = (total - availWidth) / shrinkLeeway;\n                for (i = 0; i < columns.length && total > availWidth; i++) {\n                    c = columns[i];\n                    if (!c.resizable || c.minWidth === c.width || c.width === absoluteColumnMinWidth) { continue; }\n                    var shrinkSize = Math.floor(shrinkProportion * (c.width - Math.max(c.minWidth || 0, absoluteColumnMinWidth))) || 1;\n                    total -= shrinkSize;\n                    widths[i] -= shrinkSize;\n                }\n            }\n\n            // grow\n            var previousTotal = total;\n            while (total < availWidth) {\n                var growProportion = availWidth / total;\n                for (i = 0; i < columns.length && total < availWidth; i++) {\n                    c = columns[i];\n                    if (!c.resizable || c.maxWidth <= c.width) { continue; }\n                    var growSize = Math.min(Math.floor(growProportion * c.width) - c.width, (c.maxWidth - c.width) || 1000000) || 1;\n                    total += growSize;\n                    widths[i] += growSize;\n                }\n                if (previousTotal == total) break; // if total is not changing, will result in infinite loop\n                previousTotal = total;\n            }\n\n            for (i=0; i<columns.length; i++) {\n                styleColumnWidth(i, columns[i].currentWidth = widths[i], true);\n            }\n\n            resizeCanvas();\n        }\n\n        function styleColumnWidth(index,width,styleCells) {\n            columns[index].currentWidth = width;\n            $headers.children().eq(index).css(\"width\", width - headerColumnWidthDiff);\n            if (styleCells) {\n                findCssRuleForCell(index).style.width = (width - cellWidthDiff) + \"px\";\n                findCssRule(\".\" + uid + \" .slick-row\").style.width = getRowWidth() + \"px\";\n            }\n        }\n\n        function setSortColumn(columnId, ascending) {\n            sortColumnId = columnId;\n            sortAsc = ascending;\n            var columnIndex = getColumnIndex(sortColumnId);\n\n            $headers.children().removeClass(\"slick-header-column-sorted\");\n            $headers.find(\".slick-sort-indicator\").removeClass(\"slick-sort-indicator-asc slick-sort-indicator-desc\");\n\n            if (columnIndex != null) {\n                $headers.children().eq(columnIndex)\n                    .addClass(\"slick-header-column-sorted\")\n                    .find(\".slick-sort-indicator\")\n                        .addClass(sortAsc ? \"slick-sort-indicator-asc\" : \"slick-sort-indicator-desc\");\n            }\n        }\n\n        function handleSelectedRangesChanged(e, ranges) {\n            selectedRows = [];\n            var hash = {};\n            for (var i = 0; i < ranges.length; i++) {\n                for (var j = ranges[i].fromRow; j <= ranges[i].toRow; j++) {\n                    if (!hash[j]) {  // prevent duplicates\n                        selectedRows.push(j);\n                    }\n                    hash[j] = {};\n                    for (var k = ranges[i].fromCell; k <= ranges[i].toCell; k++) {\n                        hash[j][columns[k].id] = options.selectedCellCssClass;\n                    }\n                }\n            }\n\n            setCellCssStyles(options.selectedCellCssClass, hash);\n\n            trigger(self.onSelectedRowsChanged, {rows:getSelectedRows()}, e);\n        }\n\n        function getColumns() {\n            return columns;\n        }\n\n        function setColumns(columnDefinitions) {\n            columns = columnDefinitions;\n            invalidateAllRows();\n            createColumnHeaders();\n            removeCssRules();\n            createCssRules();\n            resizeAndRender();\n            handleScroll();\n        }\n\n        function getOptions() {\n            return options;\n        }\n\n        function setOptions(args) {\n            if (!getEditorLock().commitCurrentEdit()) {\n                return;\n            }\n\n            makeActiveCellNormal();\n\n            if (options.enableAddRow !== args.enableAddRow) {\n                invalidateRow(getDataLength());\n            }\n\n            options = $.extend(options,args);\n\n            render();\n        }\n\n        function setData(newData,scrollToTop) {\n            invalidateAllRows();\n            gridData = newData;\n            if (scrollToTop)\n                scrollTo(0);\n        }\n\n        function getData() {\n            return gridData;\n        }\n\n        function getDataLength() {\n            if (gridData.getLength) {\n                return gridData.getLength();\n            }\n            else {\n                return gridData.length;\n            }\n        }\n\n        function getDataItem(i) {\n            if (gridData.getItem) {\n                return gridData.getItem(i);\n            }\n            else {\n                return gridData[i];\n            }\n        }\n\n        function getTopPanel() {\n            return $topPanel[0];\n        }\n\n        function showTopPanel() {\n            options.showTopPanel = true;\n            $topPanelScroller.slideDown(\"fast\", resizeCanvas);\n        }\n\n        function hideTopPanel() {\n            options.showTopPanel = false;\n            $topPanelScroller.slideUp(\"fast\", resizeCanvas);\n        }\n\n        function showHeaderRowColumns() {\n            options.showHeaderRow = true;\n            $headerRowScroller.slideDown(\"fast\", resizeCanvas);\n        }\n\n        function hideHeaderRowColumns() {\n            options.showHeaderRow = false;\n            $headerRowScroller.slideUp(\"fast\", resizeCanvas);\n        }\n\n        //////////////////////////////////////////////////////////////////////////////////////////////\n        // Rendering / Scrolling\n\n        function scrollTo(y) {\n            var oldOffset = offset;\n\n            page = Math.min(n-1, Math.floor(y / ph));\n            offset = Math.round(page * cj);\n            var newScrollTop = y - offset;\n\n            if (offset != oldOffset) {\n                var range = getVisibleRange(newScrollTop);\n                cleanupRows(range.top,range.bottom);\n                updateRowPositions();\n            }\n\n            if (prevScrollTop != newScrollTop) {\n                scrollDir = (prevScrollTop + oldOffset < newScrollTop + offset) ? 1 : -1;\n                $viewport[0].scrollTop = (lastRenderedScrollTop = scrollTop = prevScrollTop = newScrollTop);\n\n                trigger(self.onViewportChanged, {});\n            }\n        }\n\n        function defaultFormatter(row, cell, value, columnDef, dataContext) {\n            return (value === null || value === undefined) ? \"\" : value;\n        }\n\n        function getFormatter(column) {\n            return column.formatter ||\n                    (options.formatterFactory && options.formatterFactory.getFormatter(column)) ||\n                    defaultFormatter;\n        }\n\n        function getEditor(column) {\n            return column.editor || (options.editorFactory && options.editorFactory.getEditor(column));\n        }\n\n        function appendRowHtml(stringArray,row) {\n            var d = getDataItem(row);\n            var dataLoading = row < getDataLength() && !d;\n            var cellCss;\n            var css = \"slick-row \" +\n                (dataLoading ? \" loading\" : \"\") +\n                (row % 2 == 1 ? ' odd' : ' even');\n\n            // if the user has specified a function to provide additional per-row css classes, call it here\n            if (options.rowCssClasses) {\n                css += \" \" + options.rowCssClasses(d);\n            }\n\n            if (d instanceof Slick.GroupTotals) {\n                css += \" slick-group-totals\";\n            }\n\n            stringArray.push(\"<div class='ui-widget-content \" + css + \"' row='\" + row + \"' style='top:\" + (options.rowHeight*row-offset) + \"px'>\");\n\n            if (d instanceof Slick.Group) {\n                stringArray.push(\"<div class='slick-cell slick-group\" + (row === activeRow ? \" active\" : \"\") + \"'>\");\n                stringArray.push(\"<span class='slick-group-toggle \" + (d.collapsed ? \"collapsed\" : \"expanded\") + \"'></span>\");\n                stringArray.push(d.title);\n                stringArray.push(\"</div>\");\n            }\n            else {\n                for (var i=0, cols=columns.length; i<cols; i++) {\n                    var m = columns[i];\n\n                    cellCss = \"slick-cell c\" + i + (m.cssClass ? \" \" + m.cssClass : \"\");\n                    if (row === activeRow && i === activeCell) {\n                        cellCss += (\" active\");\n                    }\n\n                    if (!(d instanceof Slick.NonDataRow)) {\n                        // TODO:  merge them together in the setter\n                        for (var key in cellCssClasses) {\n                            if (cellCssClasses[key][row] && cellCssClasses[key][row][m.id]) {\n                                cellCss += (\" \" + cellCssClasses[key][row][m.id]);\n                            }\n                        }\n                    }\n\n                    stringArray.push(\"<div class='\" + cellCss + \"'>\");\n\n                    // if there is a corresponding row (if not, this is the Add New row or this data hasn't been loaded yet)\n                    if (d) {\n                        if (d instanceof Slick.GroupTotals) {\n                            if (m.groupTotalsFormatter) {\n                                stringArray.push(m.groupTotalsFormatter(d, m));\n                            }\n                        }\n                        else {\n                            stringArray.push(getFormatter(m)(row, i, d[m.field], m, d));\n                        }\n                    }\n\n                    stringArray.push(\"</div>\");\n                }\n            }\n\n            stringArray.push(\"</div>\");\n        }\n\n        function cleanupRows(rangeToKeep) {\n            for (var i in rowsCache) {\n                if (((i = parseInt(i, 10)) !== activeRow) && (i < rangeToKeep.top || i > rangeToKeep.bottom)) {\n                    removeRowFromCache(i);\n                }\n            }\n        }\n\n        function invalidate() {\n           updateRowCount();\n           invalidateAllRows();\n           render();\n        }\n\n        function invalidateAllRows() {\n            if (currentEditor) {\n                makeActiveCellNormal();\n            }\n            for (var row in rowsCache) {\n                removeRowFromCache(row);\n            }\n        }\n\n        function removeRowFromCache(row) {\n            var node = rowsCache[row];\n            if (!node) { return; }\n            $canvas[0].removeChild(node);\n\n            delete rowsCache[row];\n            delete postProcessedRows[row];\n            renderedRows--;\n            counter_rows_removed++;\n        }\n\n        function invalidateRows(rows) {\n            var i, rl;\n            if (!rows || !rows.length) { return; }\n            scrollDir = 0;\n            for (i=0, rl=rows.length; i<rl; i++) {\n                if (currentEditor && activeRow === i) {\n                    makeActiveCellNormal();\n                }\n\n                if (rowsCache[rows[i]]) {\n                    removeRowFromCache(rows[i]);\n                }\n            }\n        }\n\n        function invalidateRow(row) {\n            invalidateRows([row]);\n        }\n\n        function updateCell(row,cell) {\n            var cellNode = getCellNode(row,cell);\n            if (!cellNode) {\n                return;\n            }\n\n            var m = columns[cell], d = getDataItem(row);\n            if (currentEditor && activeRow === row && activeCell === cell) {\n                currentEditor.loadValue(d);\n            }\n            else {\n                cellNode.innerHTML = d ? getFormatter(m)(row, cell, d[m.field], m, d) : \"\";\n                invalidatePostProcessingResults(row);\n            }\n        }\n\n        function updateRow(row) {\n            if (!rowsCache[row]) { return; }\n\n            $(rowsCache[row]).children().each(function(i) {\n                var m = columns[i];\n                if (row === activeRow && i === activeCell && currentEditor) {\n                    currentEditor.loadValue(getDataItem(activeRow));\n                }\n                else if (getDataItem(row)) {\n                    this.innerHTML = getFormatter(m)(row, i, getDataItem(row)[m.field], m, getDataItem(row));\n                }\n                else {\n                    this.innerHTML = \"\";\n                }\n            });\n\n            invalidatePostProcessingResults(row);\n        }\n\n        function getViewportHeight() {\n            return parseFloat($.css($container[0], \"height\", true)) -\n                options.headerHeight -\n                getVBoxDelta($headers) -\n                (options.showTopPanel ? options.topPanelHeight + getVBoxDelta($topPanelScroller) : 0) -\n                (options.showHeaderRow ? options.headerRowHeight + getVBoxDelta($headerRowScroller) : 0);\n        }\n\n        function resizeCanvas() {\n            if (options.autoHeight) {\n                viewportH = options.rowHeight * (getDataLength() + (options.enableAddRow ? 1 : 0) + (options.leaveSpaceForNewRows? numVisibleRows - 1 : 0));\n            }\n            else {\n                viewportH = getViewportHeight();\n            }\n\n            numVisibleRows = Math.ceil(viewportH / options.rowHeight);\n            viewportW = parseFloat($.css($container[0], \"width\", true));\n            $viewport.height(viewportH);\n\n            var w = 0, i = columns.length;\n            while (i--) {\n                w += columns[i].currentWidth || columns[i].width;\n            }\n            setCanvasWidth(w);\n\n            updateRowCount();\n            render();\n        }\n\n        function resizeAndRender() {\n            if (options.forceFitColumns) {\n                autosizeColumns();\n            } else {\n                resizeCanvas();\n            }\n        }\n\n        function updateRowCount() {\n            var newRowCount = getDataLength() + (options.enableAddRow?1:0) + (options.leaveSpaceForNewRows?numVisibleRows-1:0);\n            var oldH = h;\n\n            // remove the rows that are now outside of the data range\n            // this helps avoid redundant calls to .removeRow() when the size of the data decreased by thousands of rows\n            var l = options.enableAddRow ? getDataLength() : getDataLength() - 1;\n            for (var i in rowsCache) {\n                if (i >= l) {\n                    removeRowFromCache(i);\n                }\n            }\n            th = Math.max(options.rowHeight * newRowCount, viewportH - scrollbarDimensions.height);\n            if (th < maxSupportedCssHeight) {\n                // just one page\n                h = ph = th;\n                n = 1;\n                cj = 0;\n            }\n            else {\n                // break into pages\n                h = maxSupportedCssHeight;\n                ph = h / 100;\n                n = Math.floor(th / ph);\n                cj = (th - h) / (n - 1);\n            }\n\n            if (h !== oldH) {\n                $canvas.css(\"height\",h);\n                scrollTop = $viewport[0].scrollTop;\n            }\n\n            var oldScrollTopInRange = (scrollTop + offset <= th - viewportH);\n\n            if (th == 0 || scrollTop == 0) {\n                page = offset = 0;\n            }\n            else if (oldScrollTopInRange) {\n                // maintain virtual position\n                scrollTo(scrollTop+offset);\n            }\n            else {\n                // scroll to bottom\n                scrollTo(th-viewportH);\n            }\n\n            if (h != oldH && options.autoHeight) {\n                resizeCanvas();\n            }\n        }\n\n        function getVisibleRange(viewportTop) {\n            if (viewportTop == null)\n                viewportTop = scrollTop;\n\n            return {\n                top: Math.floor((scrollTop+offset)/options.rowHeight),\n                bottom: Math.ceil((scrollTop+offset+viewportH)/options.rowHeight)\n            };\n        }\n\n        function getRenderedRange(viewportTop) {\n            var range = getVisibleRange(viewportTop);\n            var buffer = Math.round(viewportH/options.rowHeight);\n            var minBuffer = 3;\n\n            if (scrollDir == -1) {\n                range.top -= buffer;\n                range.bottom += minBuffer;\n            }\n            else if (scrollDir == 1) {\n                range.top -= minBuffer;\n                range.bottom += buffer;\n            }\n            else {\n                range.top -= minBuffer;\n                range.bottom += minBuffer;\n            }\n\n            range.top = Math.max(0,range.top);\n            range.bottom = Math.min(options.enableAddRow ? getDataLength() : getDataLength() - 1,range.bottom);\n\n            return range;\n        }\n\n        function renderRows(range) {\n            var i, l,\n                parentNode = $canvas[0],\n                rowsBefore = renderedRows,\n                stringArray = [],\n                rows = [],\n                startTimestamp = new Date(),\n                needToReselectCell = false;\n\n            for (i = range.top; i <= range.bottom; i++) {\n                if (rowsCache[i]) { continue; }\n                renderedRows++;\n                rows.push(i);\n                appendRowHtml(stringArray,i);\n                if (activeCellNode && activeRow === i) {\n                    needToReselectCell = true;\n                }\n                counter_rows_rendered++;\n            }\n\n            var x = document.createElement(\"div\");\n            x.innerHTML = stringArray.join(\"\");\n\n            for (i = 0, l = x.childNodes.length; i < l; i++) {\n                rowsCache[rows[i]] = parentNode.appendChild(x.firstChild);\n            }\n\n            if (needToReselectCell) {\n                activeCellNode = getCellNode(activeRow,activeCell);\n            }\n\n            if (renderedRows - rowsBefore > 5) {\n                avgRowRenderTime = (new Date() - startTimestamp) / (renderedRows - rowsBefore);\n            }\n        }\n\n        function startPostProcessing() {\n            if (!options.enableAsyncPostRender) { return; }\n            clearTimeout(h_postrender);\n            h_postrender = setTimeout(asyncPostProcessRows, options.asyncPostRenderDelay);\n        }\n\n        function invalidatePostProcessingResults(row) {\n            delete postProcessedRows[row];\n            postProcessFromRow = Math.min(postProcessFromRow,row);\n            postProcessToRow = Math.max(postProcessToRow,row);\n            startPostProcessing();\n        }\n\n        function updateRowPositions() {\n            for (var row in rowsCache) {\n                rowsCache[row].style.top = (row*options.rowHeight-offset) + \"px\";\n            }\n        }\n\n        function render() {\n            var visible = getVisibleRange();\n            var rendered = getRenderedRange();\n\n            // remove rows no longer in the viewport\n            cleanupRows(rendered);\n\n            // add new rows\n            renderRows(rendered);\n\n            postProcessFromRow = visible.top;\n            postProcessToRow = Math.min(options.enableAddRow ? getDataLength() : getDataLength() - 1, visible.bottom);\n            startPostProcessing();\n\n            lastRenderedScrollTop = scrollTop;\n            h_render = null;\n        }\n\n        function handleScroll() {\n            scrollTop = $viewport[0].scrollTop;\n            var scrollLeft = $viewport[0].scrollLeft;\n            var scrollDist = Math.abs(scrollTop - prevScrollTop);\n\n            if (scrollLeft !== prevScrollLeft) {\n                prevScrollLeft = scrollLeft;\n                $headerScroller[0].scrollLeft = scrollLeft;\n                $topPanelScroller[0].scrollLeft = scrollLeft;\n                $headerRowScroller[0].scrollLeft = scrollLeft;\n            }\n\n            if (scrollDist) {\n                scrollDir = prevScrollTop < scrollTop ? 1 : -1;\n                prevScrollTop = scrollTop;\n\n                // switch virtual pages if needed\n                if (scrollDist < viewportH) {\n                    scrollTo(scrollTop + offset);\n                }\n                else {\n                    var oldOffset = offset;\n                    page = Math.min(n - 1, Math.floor(scrollTop * ((th - viewportH) / (h - viewportH)) * (1 / ph)));\n                    offset = Math.round(page * cj);\n                    if (oldOffset != offset)\n                        invalidateAllRows();\n                }\n\n                if (h_render)\n                    clearTimeout(h_render);\n\n                if (Math.abs(lastRenderedScrollTop - scrollTop) < viewportH)\n                    render();\n                else\n                    h_render = setTimeout(render, 50);\n\n                trigger(self.onViewportChanged, {});\n            }\n\n            trigger(self.onScroll, {scrollLeft:scrollLeft, scrollTop:scrollTop});\n        }\n\n        function asyncPostProcessRows() {\n            while (postProcessFromRow <= postProcessToRow) {\n                var row = (scrollDir >= 0) ? postProcessFromRow++ : postProcessToRow--;\n                var rowNode = rowsCache[row];\n                if (!rowNode || postProcessedRows[row] || row>=getDataLength()) { continue; }\n\n                var d = getDataItem(row), cellNodes = rowNode.childNodes;\n                for (var i=0, j=0, l=columns.length; i<l; ++i) {\n                    var m = columns[i];\n                    if (m.asyncPostRender) { m.asyncPostRender(cellNodes[j], postProcessFromRow, d, m); }\n                    ++j;\n                }\n\n                postProcessedRows[row] = true;\n                h_postrender = setTimeout(asyncPostProcessRows, options.asyncPostRenderDelay);\n                return;\n            }\n        }\n\n        function addCellCssStyles(key,hash) {\n            if (cellCssClasses[key]) {\n                throw \"addCellCssStyles: cell CSS hash with key '\" + key + \"' already exists.\";\n            }\n\n            cellCssClasses[key] = hash;\n\n            for (var row in rowsCache) {\n                if (hash[row]) {\n                    if (!(getDataItem(row) instanceof Slick.NonDataRow)) {\n                        for (var columnId in hash[row]) {\n                            $(rowsCache[row]).children().eq(getColumnIndex(columnId))\n                                .addClass(hash[row][columnId]);\n                        }\n                    }\n                }\n            }\n        }\n\n        function removeCellCssStyles(key) {\n            if (!cellCssClasses[key]) {\n                return;\n            }\n\n            for (var row in rowsCache) {\n                if (cellCssClasses[key][row]) {\n                    for (var columnId in cellCssClasses[key][row]) {\n                        $(rowsCache[row]).children().eq(getColumnIndex(columnId))\n                            .removeClass(cellCssClasses[key][row][columnId]);\n                    }\n                }\n            }\n\n            delete cellCssClasses[key];\n        }\n\n        function setCellCssStyles(key,hash) {\n            removeCellCssStyles(key);\n            addCellCssStyles(key,hash);\n        }\n\n        function flashCell(row, cell, speed) {\n            speed = speed || 100;\n            if (rowsCache[row]) {\n                var $cell = $(getCellNode(row,cell));\n\n                function toggleCellClass(times) {\n                    if (!times) return;\n                    setTimeout(function() {\n                        $cell.queue(function() {\n                            $cell.toggleClass(options.cellFlashingCssClass).dequeue();\n                            toggleCellClass(times-1);\n                        });\n                    },\n                    speed);\n                }\n\n                toggleCellClass(4);\n            }\n        }\n\n        //////////////////////////////////////////////////////////////////////////////////////////////\n        // Interactivity\n\n        function getSiblingIndex(node) {\n            var idx = 0;\n            while (node && node.previousSibling) {\n                idx++;\n                node = node.previousSibling;\n            }\n            return idx;\n        }\n\n        function handleDragInit(e,dd) {\n            var cell = getCellFromEvent(e);\n            if (!cell || !cellExists(cell.row, cell.cell)) {\n                return false;\n            }\n\n            retval = trigger(self.onDragInit, dd, e);\n            if (e.isImmediatePropagationStopped()) {\n                return retval;\n            }\n        }\n\n        function handleDragStart(e,dd) {\n            var cell = getCellFromEvent(e);\n            if (!cell || !cellExists(cell.row, cell.cell)) {\n                return false;\n            }\n\n            var retval = trigger(self.onDragStart, dd, e);\n            if (e.isImmediatePropagationStopped()) {\n                return retval;\n            }\n\n            return false;\n        }\n\n        function handleDrag(e,dd) {\n            return trigger(self.onDrag, dd, e);\n        }\n\n        function handleDragEnd(e,dd) {\n            trigger(self.onDragEnd, dd, e);\n        }\n\n        function handleKeyDown(e) {\n            trigger(self.onKeyDown, {}, e);\n            var handled = e.isImmediatePropagationStopped();\n\n            if (!handled) {\n                if (!(options.shiftKeyPreventsNavigation && e.shiftKey) && !e.altKey && !e.ctrlKey) {\n                    if (e.which == 27) {\n                        if (!getEditorLock().isActive()) {\n                            return; // no editing mode to cancel, allow bubbling and default processing (exit without cancelling the event)\n                        }\n                        cancelEditAndSetFocus();\n                    }\n                    else if (e.which == 37) {\n                        navigateLeft();\n                    }\n                    else if (e.which == 39) {\n                        navigateRight();\n                    }\n                    else if (e.which == 38) {\n                        navigateUp();\n                    }\n                    else if (e.which == 40) {\n                        navigateDown();\n                    }\n                    else if (e.which == 9) {\n                        if (e.shiftKey) {\n                            navigatePrev();\n                        } else {\n                            navigateNext();\n                        }\n                    }\n\n                    else if (e.which == 13) {\n                        if (options.editable) {\n                            if (currentEditor) {\n                                // adding new row\n                                if (activeRow === getDataLength()) {\n                                    navigateDown();\n                                }\n                                else {\n                                    commitEditAndSetFocus();\n                                }\n                            } else {\n                                if (getEditorLock().commitCurrentEdit()) {\n                                    makeActiveCellEditable();\n                                }\n                            }\n                        }\n                    }\n                    else\n                        return;\n                }\n                else if (e.which == 9 && e.shiftKey && !e.ctrlKey && !e.altKey) {\n                    navigatePrev();\n                }\n                else\n                    return;\n            }\n\n            // the event has been handled so don't let parent element (bubbling/propagation) or browser (default) handle it\n            e.stopPropagation();\n            e.preventDefault();\n            try {\n                e.originalEvent.keyCode = 0; // prevent default behaviour for special keys in IE browsers (F3, F5, etc.)\n            }\n            catch (error) {} // ignore exceptions - setting the original event's keycode throws access denied exception for \"Ctrl\" (hitting control key only, nothing else), \"Shift\" (maybe others)\n        }\n\n        function handleClick(e) {\n            var cell = getCellFromEvent(e);\n            if (!cell || (currentEditor !== null && activeRow == cell.row && activeCell == cell.cell)) {\n                return;\n            }\n\n            trigger(self.onClick, {row:cell.row, cell:cell.cell}, e);\n            if (e.isImmediatePropagationStopped()) {\n                return;\n            }\n\n            if (options.enableCellNavigation && !columns[cell.cell].unselectable) {\n                if (!getEditorLock().isActive() || getEditorLock().commitCurrentEdit()) {\n                    scrollRowIntoView(cell.row,false);\n                    setActiveCellInternal(getCellNode(cell.row,cell.cell), (cell.row === getDataLength()) || options.autoEdit);\n                }\n            }\n        }\n\n        function handleContextMenu(e) {\n            var $cell = $(e.target).closest(\".slick-cell\", $canvas);\n            if ($cell.length === 0) { return; }\n\n            // are we editing this cell?\n            if (activeCellNode === $cell[0] && currentEditor !== null) { return; }\n\n            trigger(self.onContextMenu, {}, e);\n        }\n\n        function handleDblClick(e) {\n            var cell = getCellFromEvent(e);\n            if (!cell || (currentEditor !== null && activeRow == cell.row && activeCell == cell.cell)) {\n                return;\n            }\n\n            trigger(self.onDblClick, {row:cell.row, cell:cell.cell}, e);\n            if (e.isImmediatePropagationStopped()) {\n                return;\n            }\n\n            if (options.editable) {\n                gotoCell(cell.row, cell.cell, true);\n            }\n        }\n\n        function handleHeaderContextMenu(e) {\n            var $header = $(e.target).closest(\".slick-header-column\", \".slick-header-columns\");\n            var column = $header && columns[self.getColumnIndex($header.data(\"fieldId\"))];\n            trigger(self.onHeaderContextMenu, {column: column}, e);\n        }\n\n        function handleHeaderClick(e) {\n            var $header = $(e.target).closest(\".slick-header-column\", \".slick-header-columns\");\n            var column = $header && columns[self.getColumnIndex($header.data(\"fieldId\"))];\n            trigger(self.onHeaderClick, {column: column}, e);\n        }\n\n        function handleMouseEnter(e) {\n            trigger(self.onMouseEnter, {}, e);\n        }\n\n        function handleMouseLeave(e) {\n            trigger(self.onMouseLeave, {}, e);\n        }\n\n        function cellExists(row,cell) {\n            return !(row < 0 || row >= getDataLength() || cell < 0 || cell >= columns.length);\n        }\n\n        function getCellFromPoint(x,y) {\n            var row = Math.floor((y+offset)/options.rowHeight);\n            var cell = 0;\n\n            var w = 0;\n            for (var i=0; i<columns.length && w<x; i++) {\n                w += (columns[i].currentWidth || columns[i].width);\n                cell++;\n            }\n\n            if (cell < 0) {\n                cell = 0;\n            }\n\n            return {row:row,cell:cell-1};\n        }\n\n        function getCellFromEvent(e) {\n            var $cell = $(e.target).closest(\".slick-cell\", $canvas);\n            if (!$cell.length)\n                return null;\n\n            return {\n                row: $cell.parent().attr(\"row\") | 0,\n                cell: getSiblingIndex($cell[0])\n            };\n        }\n\n        function getCellNodeBox(row,cell) {\n             if (!cellExists(row,cell))\n                 return null;\n\n             var y1 = row * options.rowHeight - offset;\n             var y2 = y1 + options.rowHeight - 1;\n             var x1 = 0;\n             for (var i=0; i<cell; i++) {\n                 x1 += (columns[i].currentWidth || columns[i].width);\n             }\n             var x2 = x1 + (columns[cell].currentWidth || columns[cell].width);\n\n             return {\n                 top: y1,\n                 left: x1,\n                 bottom: y2,\n                 right: x2\n             };\n         }\n\n        //////////////////////////////////////////////////////////////////////////////////////////////\n        // Cell switching\n\n        function resetActiveCell() {\n            setActiveCellInternal(null,false);\n        }\n\n        function setFocus() {\n            // IE tries to scroll the viewport so that the item being focused is aligned to the left border\n            // IE-specific .setActive() sets the focus, but doesn't scroll\n            if ($.browser.msie) {\n                $canvas[0].setActive();\n            }\n            else {\n                $canvas[0].focus();\n            }\n        }\n\n        function scrollActiveCellIntoView() {\n            if (activeCellNode) {\n                var left = $(activeCellNode).position().left,\n                    right = left + $(activeCellNode).outerWidth(),\n                    scrollLeft = $viewport.scrollLeft(),\n                    scrollRight = scrollLeft + $viewport.width();\n\n                if (left < scrollLeft)\n                    $viewport.scrollLeft(left);\n                else if (right > scrollRight)\n                    $viewport.scrollLeft(Math.min(left, right - $viewport[0].clientWidth));\n            }\n        }\n\n        function setActiveCellInternal(newCell, editMode) {\n            if (activeCellNode !== null) {\n                makeActiveCellNormal();\n                $(activeCellNode).removeClass(\"active\");\n            }\n\n            var activeCellChanged = (activeCellNode !== newCell);\n            activeCellNode = newCell;\n\n            if (activeCellNode != null) {\n                activeRow = parseInt($(activeCellNode).parent().attr(\"row\"), 10);\n                activeCell = getSiblingIndex(activeCellNode);\n\n                $(activeCellNode).addClass(\"active\");\n\n                if (options.editable && editMode && isCellPotentiallyEditable(activeRow,activeCell)) {\n                    clearTimeout(h_editorLoader);\n\n                    if (options.asyncEditorLoading) {\n                        h_editorLoader = setTimeout(function() { makeActiveCellEditable(); }, options.asyncEditorLoadDelay);\n                    }\n                    else {\n                        makeActiveCellEditable();\n                    }\n                }\n                else {\n                      setFocus();\n                }\n            }\n            else {\n                activeRow = null;\n                activeCell = null;\n            }\n\n            if (activeCellChanged) {\n                scrollActiveCellIntoView();\n                trigger(self.onActiveCellChanged, getActiveCell());\n            }\n        }\n\n        function clearTextSelection() {\n            if (document.selection && document.selection.empty) {\n                document.selection.empty();\n            }\n            else if (window.getSelection) {\n                var sel = window.getSelection();\n                if (sel && sel.removeAllRanges) {\n                    sel.removeAllRanges();\n                }\n            }\n        }\n\n        function isCellPotentiallyEditable(row,cell) {\n            // is the data for this row loaded?\n            if (row < getDataLength() && !getDataItem(row)) {\n                return false;\n            }\n\n            // is this a special/non-data row?\n            if (getDataItem(row) instanceof Slick.NonDataRow) {\n                return false;\n            }\n\n            // are we in the Add New row?  can we create new from this cell?\n            if (columns[cell].cannotTriggerInsert && row >= getDataLength()) {\n                return false;\n            }\n\n            // does this cell have an editor?\n            if (!getEditor(columns[cell])) {\n                return false;\n            }\n\n            return true;\n        }\n\n        function makeActiveCellNormal() {\n            if (!currentEditor) { return; }\n            trigger(self.onBeforeCellEditorDestroy, {editor:currentEditor});\n            currentEditor.destroy();\n            currentEditor = null;\n\n            if (activeCellNode) {\n                $(activeCellNode).removeClass(\"editable invalid\");\n\n                if (getDataItem(activeRow)) {\n                    var column = columns[activeCell];\n                    activeCellNode.innerHTML = getFormatter(column)(activeRow, activeCell, getDataItem(activeRow)[column.field], column, getDataItem(activeRow));\n                    invalidatePostProcessingResults(activeRow);\n                }\n            }\n\n            // if there previously was text selected on a page (such as selected text in the edit cell just removed),\n            // IE can't set focus to anything else correctly\n            if ($.browser.msie) { clearTextSelection(); }\n\n            getEditorLock().deactivate(editController);\n        }\n\n        function makeActiveCellEditable(editor) {\n            if (!activeCellNode) { return; }\n            if (!options.editable) {\n                throw \"Grid : makeActiveCellEditable : should never get called when options.editable is false\";\n            }\n\n            // cancel pending async call if there is one\n            clearTimeout(h_editorLoader);\n\n            if (!isCellPotentiallyEditable(activeRow,activeCell)) {\n                return;\n            }\n\n            var columnDef = columns[activeCell];\n            var item = getDataItem(activeRow);\n\n            if (trigger(self.onBeforeEditCell, {row:activeRow, cell:activeCell, item:item, column:columnDef}) === false) {\n                setFocus();\n                return;\n            }\n\n            getEditorLock().activate(editController);\n            $(activeCellNode).addClass(\"editable\");\n\n            // don't clear the cell if a custom editor is passed through\n            if (!editor) {\n                activeCellNode.innerHTML = \"\";\n            }\n\n            currentEditor = new (editor || getEditor(columnDef))({\n                grid: self,\n                gridPosition: absBox($container[0]),\n                position: absBox(activeCellNode),\n                container: activeCellNode,\n                column: columnDef,\n                item: item || {},\n                commitChanges: commitEditAndSetFocus,\n                cancelChanges: cancelEditAndSetFocus\n            });\n\n            if (item)\n                currentEditor.loadValue(item);\n\n            serializedEditorValue = currentEditor.serializeValue();\n\n            if (currentEditor.position)\n                handleActiveCellPositionChange();\n        }\n\n        function commitEditAndSetFocus() {\n            // if the commit fails, it would do so due to a validation error\n            // if so, do not steal the focus from the editor\n            if (getEditorLock().commitCurrentEdit()) {\n                  setFocus();\n\n                if (options.autoEdit || options.alwaysNavigateDownOnCommit) {\n                    navigateDown();\n                }\n            }\n        }\n\n        function cancelEditAndSetFocus() {\n            if (getEditorLock().cancelCurrentEdit()) {\n                  setFocus();\n            }\n        }\n\n        function absBox(elem) {\n            var box = {top:elem.offsetTop, left:elem.offsetLeft, bottom:0, right:0, width:$(elem).outerWidth(), height:$(elem).outerHeight(), visible:true};\n            box.bottom = box.top + box.height;\n            box.right = box.left + box.width;\n\n            // walk up the tree\n            var offsetParent = elem.offsetParent;\n            while ((elem = elem.parentNode) != document.body) {\n                if (box.visible && elem.scrollHeight != elem.offsetHeight && $(elem).css(\"overflowY\") != \"visible\")\n                    box.visible = box.bottom > elem.scrollTop && box.top < elem.scrollTop + elem.clientHeight;\n\n                if (box.visible && elem.scrollWidth != elem.offsetWidth && $(elem).css(\"overflowX\") != \"visible\")\n                    box.visible = box.right > elem.scrollLeft && box.left < elem.scrollLeft + elem.clientWidth;\n\n                box.left -= elem.scrollLeft;\n                box.top -= elem.scrollTop;\n\n                if (elem === offsetParent) {\n                    box.left += elem.offsetLeft;\n                    box.top += elem.offsetTop;\n                    offsetParent = elem.offsetParent;\n                }\n\n                box.bottom = box.top + box.height;\n                box.right = box.left + box.width;\n            }\n\n            return box;\n        }\n\n        function getActiveCellPosition(){\n            return absBox(activeCellNode);\n        }\n\n        function getGridPosition(){\n            return absBox($container[0])\n        }\n\n        function handleActiveCellPositionChange() {\n            if (!activeCellNode) return;\n            var cellBox;\n\n            trigger(self.onActiveCellPositionChanged, {});\n\n            if (currentEditor) {\n                cellBox = cellBox || getActiveCellPosition();\n                if (currentEditor.show && currentEditor.hide) {\n                    if (!cellBox.visible)\n                        currentEditor.hide();\n                    else\n                        currentEditor.show();\n                }\n\n                if (currentEditor.position)\n                    currentEditor.position(cellBox);\n            }\n        }\n\n        function getCellEditor() {\n            return currentEditor;\n        }\n\n        function getActiveCell() {\n            if (!activeCellNode)\n                return null;\n            else\n                return {row: activeRow, cell: activeCell};\n        }\n\n        function getActiveCellNode() {\n            return activeCellNode;\n        }\n\n        function scrollRowIntoView(row, doPaging) {\n            var rowAtTop = row * options.rowHeight;\n            var rowAtBottom = (row + 1) * options.rowHeight - viewportH + (viewportHasHScroll?scrollbarDimensions.height:0);\n\n            // need to page down?\n            if ((row + 1) * options.rowHeight > scrollTop + viewportH + offset) {\n                scrollTo(doPaging ? rowAtTop : rowAtBottom);\n                render();\n            }\n\n            // or page up?\n            else if (row * options.rowHeight < scrollTop + offset) {\n                scrollTo(doPaging ? rowAtBottom : rowAtTop);\n                render();\n            }\n        }\n\n        function gotoDir(dy, dx, rollover) {\n            if (!activeCellNode || !options.enableCellNavigation) { return; }\n            if (!getEditorLock().commitCurrentEdit()) { return; }\n\n            function selectableCellFilter() {\n                return !columns[getSiblingIndex(this)].unselectable\n            }\n\n            var nextRow = rowsCache[activeRow + dy];\n            var nextCell = (nextRow && activeCell + dx >= 0)\n                    ? $(nextRow).children().eq(activeCell+dx).filter(selectableCellFilter)\n                    : null;\n\n            if (nextCell && !nextCell.length) {\n                var nodes = $(nextRow).children()\n                        .filter(function(index) { return (dx>0) ? index > activeCell + dx : index < activeCell + dx })\n                        .filter(selectableCellFilter);\n\n                if (nodes && nodes.length) {\n                nextCell = (dx>0)\n                            ? nodes.eq(0)\n                            : nodes.eq(nodes.length-1);\n                }\n            }\n\n            if (rollover && dy === 0 && !(nextRow && nextCell && nextCell.length)) {\n                if (!nextCell || !nextCell.length) {\n                    nextRow = rowsCache[activeRow + dy + ((dx>0)?1:-1)];\n                    var nodes = $(nextRow).children().filter(selectableCellFilter);\n                    if (dx > 0) {\n                        nextCell = nextRow\n                                ? nodes.eq(0)\n                                : null;\n                    }\n                    else {\n                        nextCell = nextRow\n                                ? nodes.eq(nodes.length-1)\n                                : null;\n                    }\n                }\n            }\n\n            if (nextRow && nextCell && nextCell.length) {\n                // if selecting the 'add new' row, start editing right away\n                var row = parseInt($(nextRow).attr(\"row\"), 10);\n                var isAddNewRow = (row == getDataLength());\n                scrollRowIntoView(row,!isAddNewRow);\n                setActiveCellInternal(nextCell[0], isAddNewRow || options.autoEdit);\n\n                // if no editor was created, set the focus back on the grid\n                if (!currentEditor) {\n                      setFocus();\n                }\n            }\n            else {\n                  setFocus();\n            }\n        }\n\n        function getCellNode(row, cell) {\n            if (rowsCache[row]) {\n                return $(rowsCache[row]).children().eq(cell)[0];\n            }\n            return null;\n        }\n\n        function setActiveCell(row, cell) {\n            if (row > getDataLength() || row < 0 || cell >= columns.length || cell < 0) {\n                return;\n            }\n\n            if (!options.enableCellNavigation) {\n                return;\n            }\n\n            scrollRowIntoView(row,false);\n            setActiveCellInternal(getCellNode(row,cell),false);\n        }\n\n        function canCellBeSelected(row,cell) {\n            return row < getDataLength()\n                    && row >= 0\n                    && cell < columns.length\n                    && cell >= 0\n                    && !columns[cell].unselectable\n                    && !(getDataItem(row) instanceof Slick.NonDataRow);\n        }\n\n        function gotoCell(row, cell, forceEdit) {\n            if (row > getDataLength() || row < 0 || cell >= columns.length || cell < 0) { return; }\n            if (!options.enableCellNavigation || columns[cell].unselectable) { return; }\n\n            if (!getEditorLock().commitCurrentEdit()) { return; }\n\n            scrollRowIntoView(row,false);\n\n            var newCell = null;\n            if (!columns[cell].unselectable) {\n                newCell = getCellNode(row,cell);\n            }\n\n            // if selecting the 'add new' row, start editing right away\n            setActiveCellInternal(newCell, forceEdit || (row === getDataLength()) || options.autoEdit);\n\n            // if no editor was created, set the focus back on the grid\n            if (!currentEditor) {\n                  setFocus();\n            }\n        }\n\n        function navigateUp() {\n            gotoDir(-1, 0, false);\n        }\n\n        function navigateDown() {\n            gotoDir(1, 0, false);\n        }\n\n        function navigateLeft() {\n            gotoDir(0, -1, false);\n        }\n\n        function navigateRight() {\n            gotoDir(0, 1, false);\n        }\n\n        function navigatePrev() {\n            gotoDir(0, -1, true);\n        }\n\n        function navigateNext() {\n            gotoDir(0, 1, true);\n        }\n\n        //////////////////////////////////////////////////////////////////////////////////////////////\n        // IEditor implementation for the editor lock\n\n        function commitCurrentEdit() {\n            var item = getDataItem(activeRow);\n            var column = columns[activeCell];\n\n            if (currentEditor) {\n                if (currentEditor.isValueChanged()) {\n                    var validationResults = currentEditor.validate();\n\n                    if (validationResults.valid) {\n                        if (activeRow < getDataLength()) {\n                            var editCommand = {\n                                row: activeRow,\n                                cell: activeCell,\n                                editor: currentEditor,\n                                serializedValue: currentEditor.serializeValue(),\n                                prevSerializedValue: serializedEditorValue,\n                                execute: function() {\n                                    this.editor.applyValue(item,this.serializedValue);\n                                    updateRow(this.row);\n                                },\n                                undo: function() {\n                                    this.editor.applyValue(item,this.prevSerializedValue);\n                                    updateRow(this.row);\n                                }\n                            };\n\n                            if (options.editCommandHandler) {\n                                makeActiveCellNormal();\n                                options.editCommandHandler(item,column,editCommand);\n\n                            }\n                            else {\n                                editCommand.execute();\n                                makeActiveCellNormal();\n                            }\n\n                            trigger(self.onCellChange, {\n                                row: activeRow,\n                                cell: activeCell,\n                                item: item\n                            });\n                        }\n                        else {\n                            var newItem = {};\n                            currentEditor.applyValue(newItem,currentEditor.serializeValue());\n                            makeActiveCellNormal();\n                            trigger(self.onAddNewRow, {item:newItem, column:column});\n                        }\n\n                        // check whether the lock has been re-acquired by event handlers\n                        return !getEditorLock().isActive();\n                    }\n                    else {\n                        // TODO: remove and put in onValidationError handlers in examples\n                        $(activeCellNode).addClass(\"invalid\");\n                        $(activeCellNode).stop(true,true).effect(\"highlight\", {color:\"red\"}, 300);\n\n                        trigger(self.onValidationError, {\n                            editor: currentEditor,\n                            cellNode: activeCellNode,\n                            validationResults: validationResults,\n                            row: activeRow,\n                            cell: activeCell,\n                            column: column\n                        });\n\n                        currentEditor.focus();\n                        return false;\n                    }\n                }\n\n                makeActiveCellNormal();\n            }\n            return true;\n        }\n\n        function cancelCurrentEdit() {\n            makeActiveCellNormal();\n            return true;\n        }\n\n        function rowsToRanges(rows) {\n            var ranges = [];\n            var lastCell = columns.length - 1;\n            for (var i = 0; i < rows.length; i++) {\n                ranges.push(new Slick.Range(rows[i], 0, rows[i], lastCell));\n            }\n            return ranges;\n        }\n\n        function getSelectedRows() {\n            if (!selectionModel) {\n                throw \"Selection model is not set\";\n            }\n            return selectedRows;\n        }\n\n        function setSelectedRows(rows) {\n            if (!selectionModel) {\n                throw \"Selection model is not set\";\n            }\n            selectionModel.setSelectedRanges(rowsToRanges(rows));\n        }\n\n\n        //////////////////////////////////////////////////////////////////////////////////////////////\n        // Debug\n\n        this.debug = function() {\n            var s = \"\";\n\n            s += (\"\\n\" + \"counter_rows_rendered:  \" + counter_rows_rendered);\n            s += (\"\\n\" + \"counter_rows_removed:  \" + counter_rows_removed);\n            s += (\"\\n\" + \"renderedRows:  \" + renderedRows);\n            s += (\"\\n\" + \"numVisibleRows:  \" + numVisibleRows);\n            s += (\"\\n\" + \"maxSupportedCssHeight:  \" + maxSupportedCssHeight);\n            s += (\"\\n\" + \"n(umber of pages):  \" + n);\n            s += (\"\\n\" + \"(current) page:  \" + page);\n            s += (\"\\n\" + \"page height (ph):  \" + ph);\n            s += (\"\\n\" + \"scrollDir:  \" + scrollDir);\n\n            alert(s);\n        };\n\n        // a debug helper to be able to access private members\n        this.eval = function(expr) {\n            return eval(expr);\n        };\n\n        //////////////////////////////////////////////////////////////////////////////////////////////\n        // Public API\n\n        $.extend(this, {\n            \"slickGridVersion\": \"2.0a1\",\n\n            // Events\n            \"onScroll\":                     new Slick.Event(),\n            \"onSort\":                       new Slick.Event(),\n            \"onHeaderContextMenu\":          new Slick.Event(),\n            \"onHeaderClick\":                new Slick.Event(),\n            \"onMouseEnter\":                 new Slick.Event(),\n            \"onMouseLeave\":                 new Slick.Event(),\n            \"onClick\":                      new Slick.Event(),\n            \"onDblClick\":                   new Slick.Event(),\n            \"onContextMenu\":                new Slick.Event(),\n            \"onKeyDown\":                    new Slick.Event(),\n            \"onAddNewRow\":                  new Slick.Event(),\n            \"onValidationError\":            new Slick.Event(),\n            \"onViewportChanged\":            new Slick.Event(),\n            \"onColumnsReordered\":           new Slick.Event(),\n            \"onColumnsResized\":             new Slick.Event(),\n            \"onCellChange\":                 new Slick.Event(),\n            \"onBeforeEditCell\":             new Slick.Event(),\n            \"onBeforeCellEditorDestroy\":    new Slick.Event(),\n            \"onBeforeDestroy\":              new Slick.Event(),\n            \"onActiveCellChanged\":          new Slick.Event(),\n            \"onActiveCellPositionChanged\":  new Slick.Event(),\n            \"onDragInit\":                   new Slick.Event(),\n            \"onDragStart\":                  new Slick.Event(),\n            \"onDrag\":                       new Slick.Event(),\n            \"onDragEnd\":                    new Slick.Event(),\n            \"onSelectedRowsChanged\":        new Slick.Event(),\n\n            // Methods\n            \"registerPlugin\":               registerPlugin,\n            \"unregisterPlugin\":             unregisterPlugin,\n            \"getColumns\":                   getColumns,\n            \"setColumns\":                   setColumns,\n            \"getColumnIndex\":               getColumnIndex,\n            \"updateColumnHeader\":           updateColumnHeader,\n            \"setSortColumn\":                setSortColumn,\n            \"autosizeColumns\":              autosizeColumns,\n            \"getOptions\":                   getOptions,\n            \"setOptions\":                   setOptions,\n            \"getData\":                      getData,\n            \"getDataLength\":                getDataLength,\n            \"getDataItem\":                  getDataItem,\n            \"setData\":                      setData,\n            \"getSelectionModel\":            getSelectionModel,\n            \"setSelectionModel\":            setSelectionModel,\n            \"getSelectedRows\":              getSelectedRows,\n            \"setSelectedRows\":              setSelectedRows,\n\n            \"render\":                       render,\n            \"invalidate\":                   invalidate,\n            \"invalidateRow\":                invalidateRow,\n            \"invalidateRows\":               invalidateRows,\n            \"invalidateAllRows\":            invalidateAllRows,\n            \"updateCell\":                   updateCell,\n            \"updateRow\":                    updateRow,\n            \"getViewport\":                  getVisibleRange,\n            \"resizeCanvas\":                 resizeCanvas,\n            \"updateRowCount\":               updateRowCount,\n            \"scrollRowIntoView\":            scrollRowIntoView,\n            \"getCanvasNode\":                getCanvasNode,\n\n            \"getCellFromPoint\":             getCellFromPoint,\n            \"getCellFromEvent\":             getCellFromEvent,\n            \"getActiveCell\":                getActiveCell,\n            \"setActiveCell\":                setActiveCell,\n            \"getActiveCellNode\":            getActiveCellNode,\n            \"getActiveCellPosition\":        getActiveCellPosition,\n            \"resetActiveCell\":              resetActiveCell,\n            \"editActiveCell\":               makeActiveCellEditable,\n            \"getCellEditor\":                getCellEditor,\n            \"getCellNode\":                  getCellNode,\n            \"getCellNodeBox\":               getCellNodeBox,\n            \"canCellBeSelected\":            canCellBeSelected,\n            \"navigatePrev\":                 navigatePrev,\n            \"navigateNext\":                 navigateNext,\n            \"navigateUp\":                   navigateUp,\n            \"navigateDown\":                 navigateDown,\n            \"navigateLeft\":                 navigateLeft,\n            \"navigateRight\":                navigateRight,\n            \"gotoCell\":                     gotoCell,\n            \"getTopPanel\":                  getTopPanel,\n            \"showTopPanel\":                 showTopPanel,\n            \"hideTopPanel\":                 hideTopPanel,\n            \"showHeaderRowColumns\":         showHeaderRowColumns,\n            \"hideHeaderRowColumns\":         hideHeaderRowColumns,\n            \"getHeaderRow\":                 getHeaderRow,\n            \"getHeaderRowColumn\":           getHeaderRowColumn,\n            \"getGridPosition\":              getGridPosition,\n            \"flashCell\":                    flashCell,\n            \"addCellCssStyles\":             addCellCssStyles,\n            \"setCellCssStyles\":             setCellCssStyles,\n            \"removeCellCssStyles\":          removeCellCssStyles,\n\n            \"destroy\":                      destroy,\n\n            // IEditor implementation\n            \"getEditorLock\":                getEditorLock,\n            \"getEditController\":            getEditController\n        });\n\n        init();\n    }\n}(jQuery));\n"
  },
  {
    "path": "dirigible/shared/static/splitter/splitter.js",
    "content": "/*\n * jQuery.splitter.js - two-pane splitter window plugin\n *\n * version 1.51 (2009/01/09) \n * \n * Dual licensed under the MIT and GPL licenses: \n *   http://www.opensource.org/licenses/mit-license.php \n *   http://www.gnu.org/licenses/gpl.html \n */\n\n/**\n * The splitter() plugin implements a two-pane resizable splitter window.\n * The selected elements in the jQuery object are converted to a splitter;\n * each selected element should have two child elements, used for the panes\n * of the splitter. The plugin adds a third child element for the splitbar.\n * \n * For more details see: http://methvin.com/splitter/\n *\n *\n * @example $('#MySplitter').splitter();\n * @desc Create a vertical splitter with default settings \n *\n * @example $('#MySplitter').splitter({type: 'h', accessKey: 'M'});\n * @desc Create a horizontal splitter resizable via Alt+Shift+M\n *\n * @name splitter\n * @type jQuery\n * @param Object options Options for the splitter (not required)\n * @cat Plugins/Splitter\n * @return jQuery\n * @author Dave Methvin (dave.methvin@gmail.com)\n */\n ;(function($){\n \n $.fn.splitter = function(args){\n\targs = args || {};\n\treturn this.each(function() {\n\t\tvar zombie;\t\t// left-behind splitbar for outline resizes\n\t\tfunction startSplitMouse(evt) {\n\t\t\tif ( opts.outline )\n\t\t\t\tzombie = zombie || bar.clone(false).insertAfter(A);\n\t\t\tpanes.css(\"-webkit-user-select\", \"none\");\t// Safari selects A/B text on a move\n\t\t\tbar.addClass(opts.activeClass);\n\t\t\tA._posSplit = A[0][opts.pxSplit] - evt[opts.eventPos];\n\t\t\t$(document)\n\t\t\t\t.bind(\"mousemove\", doSplitMouse)\n\t\t\t\t.bind(\"mouseup\", endSplitMouse);\n\t\t}\n\t\tfunction doSplitMouse(evt) {\n\t\t\tvar newPos = A._posSplit+evt[opts.eventPos];\n\t\t\tif ( opts.outline ) {\n\t\t\t\tnewPos = Math.max(0, Math.min(newPos, splitter._DA - bar._DA));\n\t\t\t\tbar.css(opts.origin, newPos);\n\t\t\t} else \n\t\t\t\tresplit(newPos);\n\t\t}\n\t\tfunction endSplitMouse(evt) {\n\t\t\tbar.removeClass(opts.activeClass);\n\t\t\tvar newPos = A._posSplit+evt[opts.eventPos];\n\t\t\tif ( opts.outline ) {\n\t\t\t\tzombie.remove(); zombie = null;\n\t\t\t\tresplit(newPos);\n\t\t\t}\n\t\t\tpanes.css(\"-webkit-user-select\", \"text\");\t// let Safari select text again\n\t\t\t$(document)\n\t\t\t\t.unbind(\"mousemove\", doSplitMouse)\n\t\t\t\t.unbind(\"mouseup\", endSplitMouse);\n\t\t}\n\t\tfunction resplit(newPos) {\n\t\t\t// Constrain new splitbar position to fit pane size limits\n\t\t\tnewPos = Math.max(A._min, splitter._DA - B._max, \n\t\t\t\t\tMath.min(newPos, A._max, splitter._DA - bar._DA - B._min));\n\t\t\t// Resize/position the two panes\n\t\t\tbar._DA = bar[0][opts.pxSplit];\t\t// bar size may change during dock\n\t\t\tbar.css(opts.origin, newPos).css(opts.fixed, splitter._DF);\n\t\t\tA.css(opts.origin, 0).css(opts.split, newPos).css(opts.fixed,  splitter._DF);\n\t\t\tB.css(opts.origin, newPos+bar._DA)\n\t\t\t\t.css(opts.split, splitter._DA-bar._DA-newPos).css(opts.fixed,  splitter._DF);\n\t\t\t// IE fires resize for us; all others pay cash\n\t\t\tif ( !$.browser.msie )\n\t\t\t\tpanes.trigger(\"resize\");\n\t\t}\n\t\tfunction dimSum(jq, dims) {\n\t\t\t// Opera returns -1 for missing min/max width, turn into 0\n\t\t\tvar sum = 0;\n\t\t\tfor ( var i=1; i < arguments.length; i++ )\n\t\t\t\tsum += Math.max(parseInt(jq.css(arguments[i])) || 0, 0);\n\t\t\treturn sum;\n\t\t}\n\t\t\n\t\t// Determine settings based on incoming opts, element classes, and defaults\n\t\tvar vh = (args.splitHorizontal? 'h' : args.splitVertical? 'v' : args.type) || 'v';\n\t\tvar opts = $.extend({\n\t\t\tactiveClass: 'active',\t// class name for active splitter\n\t\t\tpxPerKey: 8,\t\t\t// splitter px moved per keypress\n\t\t\ttabIndex: 0,\t\t\t// tab order indicator\n\t\t\taccessKey: ''\t\t\t// accessKey for splitbar\n\t\t},{\n\t\t\tv: {\t\t\t\t\t// Vertical splitters:\n\t\t\t\tkeyLeft: 39, keyRight: 37, cursor: \"e-resize\",\n\t\t\t\tsplitbarClass: \"vsplitbar\", outlineClass: \"voutline\",\n\t\t\t\ttype: 'v', eventPos: \"pageX\", origin: \"left\",\n\t\t\t\tsplit: \"width\",  pxSplit: \"offsetWidth\",  side1: \"Left\", side2: \"Right\",\n\t\t\t\tfixed: \"height\", pxFixed: \"offsetHeight\", side3: \"Top\",  side4: \"Bottom\"\n\t\t\t},\n\t\t\th: {\t\t\t\t\t// Horizontal splitters:\n\t\t\t\tkeyTop: 40, keyBottom: 38,  cursor: \"n-resize\",\n\t\t\t\tsplitbarClass: \"hsplitbar\", outlineClass: \"houtline\",\n\t\t\t\ttype: 'h', eventPos: \"pageY\", origin: \"top\",\n\t\t\t\tsplit: \"height\", pxSplit: \"offsetHeight\", side1: \"Top\",  side2: \"Bottom\",\n\t\t\t\tfixed: \"width\",  pxFixed: \"offsetWidth\",  side3: \"Left\", side4: \"Right\"\n\t\t\t}\n\t\t}[vh], args);\n\n\t\t// Create jQuery object closures for splitter and both panes\n\t\tvar splitter = $(this).css({position: \"relative\"});\n\t\tvar panes = $(\">*\", splitter[0]).css({\n\t\t\tposition: \"absolute\", \t\t\t// positioned inside splitter container\n\t\t\t\"z-index\": \"1\",\t\t\t\t\t// splitbar is positioned above\n\t\t\t\"-moz-outline-style\": \"none\"\t// don't show dotted outline\n\t\t});\n\t\tvar A = $(panes[0]);\t\t// left  or top\n\t\tvar B = $(panes[1]);\t\t// right or bottom\n\n\t\t// Focuser element, provides keyboard support; title is shown by Opera accessKeys\n\t\tvar focuser = $('<a href=\"javascript:void(0)\"></a>')\n\t\t\t.attr({accessKey: opts.accessKey, tabIndex: opts.tabIndex, title: opts.splitbarClass})\n\t\t\t.bind($.browser.opera?\"click\":\"focus\", function(){ this.focus(); bar.addClass(opts.activeClass) })\n\t\t\t.bind(\"keydown\", function(e){\n\t\t\t\tvar key = e.which || e.keyCode;\n\t\t\t\tvar dir = key==opts[\"key\"+opts.side1]? 1 : key==opts[\"key\"+opts.side2]? -1 : 0;\n\t\t\t\tif ( dir )\n\t\t\t\t\tresplit(A[0][opts.pxSplit]+dir*opts.pxPerKey, false);\n\t\t\t})\n\t\t\t.bind(\"blur\", function(){ bar.removeClass(opts.activeClass) });\n\t\t\t\n\t\t// Splitbar element, can be already in the doc or we create one\n\t\tvar bar = $(panes[2] || '<div></div>')\n\t\t\t.insertAfter(A).css(\"z-index\", \"100\").append(focuser)\n\t\t\t.attr({\"class\": opts.splitbarClass, unselectable: \"on\"})\n\t\t\t.css({position: \"absolute\",\t\"user-select\": \"none\", \"-webkit-user-select\": \"none\",\n\t\t\t\t\"-khtml-user-select\": \"none\", \"-moz-user-select\": \"none\"})\n\t\t\t.bind(\"mousedown\", startSplitMouse);\n\t\t// Use our cursor unless the style specifies a non-default cursor\n\t\tif ( /^(auto|default|)$/.test(bar.css(\"cursor\")) )\n\t\t\tbar.css(\"cursor\", opts.cursor);\n\n\t\t// Cache several dimensions for speed, rather than re-querying constantly\n\t\tbar._DA = bar[0][opts.pxSplit];\n\t\tsplitter._PBF = $.boxModel? dimSum(splitter, \"border\"+opts.side3+\"Width\", \"border\"+opts.side4+\"Width\") : 0;\n\t\tsplitter._PBA = $.boxModel? dimSum(splitter, \"border\"+opts.side1+\"Width\", \"border\"+opts.side2+\"Width\") : 0;\n\t\tA._pane = opts.side1;\n\t\tB._pane = opts.side2;\n\t\t$.each([A,B], function(){\n\t\t\tthis._min = opts[\"min\"+this._pane] || dimSum(this, \"min-\"+opts.split);\n\t\t\tthis._max = opts[\"max\"+this._pane] || dimSum(this, \"max-\"+opts.split) || 9999;\n\t\t\tthis._init = opts[\"size\"+this._pane]===true ?\n\t\t\t\tparseInt($.curCSS(this[0],opts.split)) : opts[\"size\"+this._pane];\n\t\t});\n\t\t\n\t\t// Determine initial position, get from cookie if specified\n\t\tvar initPos = A._init;\n\t\tif ( !isNaN(B._init) )\t// recalc initial B size as an offset from the top or left side\n\t\t\tinitPos = splitter[0][opts.pxSplit] - splitter._PBA - B._init - bar._DA;\n\t\tif ( opts.cookie ) {\n\t\t\tif ( !$.cookie )\n\t\t\t\talert('jQuery.splitter(): jQuery cookie plugin required');\n\t\t\tvar ckpos = parseInt($.cookie(opts.cookie));\n\t\t\tif ( !isNaN(ckpos) )\n\t\t\t\tinitPos = ckpos;\n\t\t\t$(window).bind(\"unload\", function(){\n\t\t\t\tvar state = String(bar.css(opts.origin));\t// current location of splitbar\n\t\t\t\t$.cookie(opts.cookie, state, {expires: opts.cookieExpires || 365, \n\t\t\t\t\tpath: opts.cookiePath || document.location.pathname});\n\t\t\t});\n\t\t}\n\t\tif ( isNaN(initPos) )\t// King Solomon's algorithm\n\t\t\tinitPos = Math.round((splitter[0][opts.pxSplit] - splitter._PBA - bar._DA)/2);\n\n\t\t// Resize event propagation and splitter sizing\n\t\tif ( opts.anchorToWindow ) {\n\t\t\t// Account for margin or border on the splitter container and enforce min height\n\t\t\tsplitter._hadjust = dimSum(splitter, \"borderTopWidth\", \"borderBottomWidth\", \"marginBottom\");\n\t\t\tsplitter._hmin = Math.max(dimSum(splitter, \"minHeight\"), 20);\n\t\t\t$(window).bind(\"resize\", function(){\n\t\t\t\tvar top = splitter.offset().top;\n\t\t\t\tvar wh = $(window).height();\n\t\t\t\tsplitter.css(\"height\", Math.max(wh-top-splitter._hadjust, splitter._hmin)+\"px\");\n\t\t\t\tif ( !$.browser.msie ) splitter.trigger(\"resize\");\n\t\t\t}).trigger(\"resize\");\n\t\t}\n\t\telse if ( opts.resizeToWidth && !$.browser.msie )\n\t\t\t$(window).bind(\"resize\", function(){\n\t\t\t\tsplitter.trigger(\"resize\"); \n\t\t\t});\n\n\t\t// Resize event handler; triggered immediately to set initial position\n\t\tsplitter.bind(\"resize\", function(e, size){\n\t\t\t// Custom events bubble in jQuery 1.3; don't Yo Dawg\n\t\t\tif ( e.target != this ) return;\n\t\t\t// Determine new width/height of splitter container\n\t\t\tsplitter._DF = splitter[0][opts.pxFixed] - splitter._PBF;\n\t\t\tsplitter._DA = splitter[0][opts.pxSplit] - splitter._PBA;\n\t\t\t// Bail if splitter isn't visible or content isn't there yet\n\t\t\tif ( splitter._DF <= 0 || splitter._DA <= 0 ) return;\n\t\t\t// Re-divvy the adjustable dimension; maintain size of the preferred pane\n\t\t\tresplit(!isNaN(size)? size : (!(opts.sizeRight||opts.sizeBottom)? A[0][opts.pxSplit] :\n\t\t\t\tsplitter._DA-B[0][opts.pxSplit]-bar._DA));\n\t\t}).trigger(\"resize\" , [initPos]);\n\t});\n};\n\n})(jQuery);"
  },
  {
    "path": "dirigible/shared/templates/403.html",
    "content": "{% extends \"error_page.html\" %}\n\n{% block title %}\n    Access Forbidden: Dirigible\n{% endblock %}\n\n{% block error_title %}403 Access Forbidden{% endblock %}\n{% block error_text %}\n        This page is private and belongs to someone else.\n{% endblock %}\n\n{% block error_recovery_link %}\n<a id=\"id_link_home\" href=\"/\">Return to Dirigible home</a>\n{% endblock %}"
  },
  {
    "path": "dirigible/shared/templates/404.html",
    "content": "{% extends \"error_page.html\" %}\n\n{% block title %}\n    Page not found: Dirigible\n{% endblock %}\n\n{% block error_title %}404 Not Found{% endblock %}\n{% block error_text %}\n        The page that you seek<br/>\n        can't be found; lost in the clouds.<br/>\n        Please look somewhere else\n{% endblock %}\n\n{% block error_recovery_link %}\n<a id=\"id_link_home\" href=\"/\">Return to Dirigible home</a>\n{% endblock %}"
  },
  {
    "path": "dirigible/shared/templates/500.html",
    "content": "{% extends \"error_page.html\" %}\n\n{% block title %}\n    Server error: Dirigible\n{% endblock %}\n\n{% block error_title %}500 Internal Server Error{% endblock %}\n{% block error_text %}\n        Sorry, something has gone wrong!\n        The Dirigible team have notified and will look into the problem ASAP.\n{% endblock %}\n\n{% block error_recovery_link %}\n<a id=\"id_link_home\" href=\"/\">Return to Dirigible home</a>\n{% endblock %}"
  },
  {
    "path": "dirigible/shared/templates/base.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n<html>\n    <head>\n        <title>{% block title %}{% endblock %}</title>\n\n\n        <link rel=\"stylesheet\" href=\"/static/dirigible/styles/base.css\" type=\"text/css\" media=\"screen\" charset=\"utf-8\" />\n\n        <script type=\"text/javascript\" src=\"/static/jquery/jquery-1.5.2.min.js\"></script>\n        <script type=\"text/javascript\" src=\"/static/jquery/jquery-ui-1.8.10.custom.min.js\"></script>\n        <link rel=\"stylesheet\" href=\"/static/jquery/jquery-ui-1.8.10.custom.css\" type=\"text/css\" media=\"screen\" charset=\"utf-8\" />\n        \n        <script type=\"text/javascript\">\n            var urls = {};\n        </script>\n        <script type=\"text/javascript\" src=\"/static/dirigible/scripts/feedback_dialog.js\"></script>\n        <script>\nvar csrfToken = \"{{ csrf_token }}\";\nvar csrfSafeMethod = function(method) {\n    // these HTTP methods do not require CSRF protection\n    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));\n}\n$.ajaxSetup({\n    beforeSend: function(xhr, settings) {\n        if (!csrfSafeMethod(settings.type) && !this.crossDomain) {\n            xhr.setRequestHeader(\"X-CSRFToken\", csrfToken);\n        }\n    }\n});\n        </script>\n\n        {% block head %}\n        {% endblock %}\n\n    </head>\n\n    <body>\n\t</p>\n        {% block body %}\n        {% endblock %}\n    </body>\n</html>\n"
  },
  {
    "path": "dirigible/shared/templates/error_page.html",
    "content": "{% extends \"non_sheet_page_small_logo.html\" %}\n\n{% block head %}\n    {{ block.super }}\n    <link rel=\"stylesheet\" href=\"/static/dirigible/styles/error.css\" type=\"text/css\" media=\"screen\" charset=\"utf-8\" />\n{% endblock %}\n\n{% block content %}\n    <div class=\"clear\"></div>\n    <br />\n    <div id=\"id_error_wrap\" class=\"grey_rounded_corner_box\">\n        <h3 id=\"id_server_error_title\">{% block error_title %}{% endblock %}</h3>\n\n        <p id=\"id_server_error_text\">\n            {% block error_text %}{% endblock %}\n        </p>\n        <p id=\"id_recovery_block\">\n            {% block error_recovery_link %}{% endblock %}\n        </p>\n    </div>\n{% endblock %}\n"
  },
  {
    "path": "dirigible/shared/templates/footer_links_include.html",
    "content": "<div id=\"id_footer_links\" class=\"centered-block\">\r\n    <div id=\"id_useful_information\">\r\n        <a href=\"/oss/\">Open Source</a>\r\n    </div>\r\n</div>\r\n"
  },
  {
    "path": "dirigible/shared/templates/header_links_include.html",
    "content": "<div id='id_header_links'>\n    <span id=\"id_account_buttons_wrap\">\n        {% if user and user.username %}\n            <a id='id_account_link' href='{% url 'front_page' %}'>My account</a>\n            <span class='separator'>|</span>\n            <a id='id_logout_link' href='{% url 'logout' %}'>Log out</a>\n        {% else %}\n            <a id='id_signup_link' href='{% url 'registration_register' %}'>Sign up</a>\n            <span class='separator'>|</span>\n            <a id='id_login_link' href='{% url 'login' %}'>Log in</a>\n        {% endif %}\n    </span>\n    {% if sheet %}\n    <!--[if IE]>\n        <span id=\"id_ie_warning\" title=\"Sorry, we're currently working on fixing some bugs with Internet Explorer. You may be better off using another browser if you can.\" >Internet Explorer Warning</span>\n    <![endif]-->\n    {% endif %}\n\n    <div id='id_top_links'>\n        <a id='id_help_link' href=\"/documentation/\">Help</a>\n    </div>\n    <script type=\"text/javascript\">\n        $(function() {\n            $.extend(\n                urls,\n                {\n                    feedback: '{% url 'feedback_submit' %}'\n                }\n            );\n            {% if user and user.username %}\n                Dirigible.FeedbackDialog.Initialise(urls, '{{ user.username }}');\n            {% else %}\n                Dirigible.FeedbackDialog.Initialise(urls, '');\n            {% endif %}\n        });\n    </script>\n</div>\n<div id=\"id_feedback_dialog\" title=\"Help us improve\" style=\"display:none\">\n    <div id=\"id_feedback_dialog_blurb_big\">\n        It's always a pleasure to hear from you!\n    </div>\n    <div id=\"id_feedback_dialog_blurb_small\">\n        Ask us a question, or tell us what you love or hate about Dirigible:\n    </div>\n    <textarea id=\"id_feedback_dialog_text\" rows=\"6\"></textarea>\n    <input id=\"id_feedback_dialog_email_address\" type=\"text\" class=\"email_prompt\"/>\n    <div id=\"id_feedback_dialog_error\" class=\"hidden\">\n        Sorry, there was an error connecting to the server. <br/>Please try again in a few moments...\n    </div>\n    <div class=\"dialog_buttons\">\n        <img id=\"id_feedback_dialog_spinner\" src=\"/static/dirigible/images/spinner-small-white-bg.gif\" />\n        <input value=\"OK\" id=\"id_feedback_dialog_ok_button\" type=\"button\">\n        <input value=\"Cancel\" id=\"id_feedback_dialog_cancel_button\" type=\"button\">\n    </div>\n</div>\n"
  },
  {
    "path": "dirigible/shared/templates/info_page.html",
    "content": "{% extends \"non_sheet_page_small_logo.html\" %}\n\n{% block head %}\n    {{ block.super }}\n    <link rel=\"stylesheet\" href=\"/static/dirigible/styles/info_page.css\" type=\"text/css\" media=\"screen\" charset=\"utf-8\" />\n{% endblock %}\n\n\n{% block content %}\n\n    <div id=\"id_extra_banner_spacing\"></div>\n\n    <div id=\"id_main_background\">\n        <div id=\"id_content_background\" class=\"centered-block\">\n\n            <div id=\"id_text\">\n\n                {% block middle %}\n                {% endblock %}\n\n            </div>\n\n        </div>\n    </div>\n\n{% endblock %}\n"
  },
  {
    "path": "dirigible/shared/templates/non_sheet_page_small_logo.html",
    "content": "{% extends \"base.html\" %}\r\n\r\n{% block head %}\r\n    {{ block.super }}\r\n    <link rel=\"stylesheet\" href=\"/static/dirigible/styles/non_sheet_page.css\" type=\"text/css\" media=\"screen\" charset=\"utf-8\" />\r\n{% endblock %}\r\n\r\n\r\n{% block body %}\r\n<div id=\"id_container\">\r\n    <div id=\"id_content\">\r\n\r\n        <div id=\"id_header\" class=\"centered-block\">\r\n\r\n            <a href=\"/\">\r\n                <img id=\"id_small_header_logo\" src=\"/static/dirigible/images/logo_transparent_289x66.png\" alt=\"Dirigible logo\" title=\"Dirigible logo\" />\r\n            </a>\r\n\r\n            {% include \"header_links_include.html\" %}\r\n        </div>\r\n\r\n\r\n        {% block content %}\r\n        {% endblock %}\r\n\r\n        {% include \"footer_links_include.html\" %}\r\n\r\n    </div>\r\n</div>\r\n{% endblock %}\r\n"
  },
  {
    "path": "dirigible/shared/tests/__init__.py",
    "content": "\n"
  },
  {
    "path": "dirigible/shared/tests/test_views.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\r\n# See LICENSE.md\r\n#\r\n\r\ntry:\r\n    import unittest2 as unittest\r\nexcept ImportError:\r\n    import unittest\r\n\r\nfrom django.http import HttpRequest, HttpResponsePermanentRedirect\r\n\r\nfrom shared.views import redirect_to\r\n\r\n\r\nclass TestRedirectTo(unittest.TestCase):\r\n\r\n    def test_redirect_to_no_params(self):\r\n        request = HttpRequest()\r\n        url = \"http://blog.projectdirigible.com/\"\r\n        response = redirect_to(request, url)\r\n        self.assertTrue(isinstance(response, HttpResponsePermanentRedirect))\r\n        self.assertEquals(response.status_code, 301)\r\n        self.assertEquals(response['Location'], url)\r\n\r\n\r\n    def test_redirect_to_with_params(self):\r\n        request = HttpRequest()\r\n        request.META[\"QUERY_STRING\"] = 'feed=rss2'\r\n        url = \"http://blog.projectdirigible.com/\"\r\n        response = redirect_to(request, url)\r\n        self.assertTrue(isinstance(response, HttpResponsePermanentRedirect))\r\n        self.assertEquals(response.status_code, 301)\r\n        self.assertEquals(response['Location'], url + '?feed=rss2')\r\n"
  },
  {
    "path": "dirigible/shared/views.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\nfrom django.http import HttpResponsePermanentRedirect\n\n\n# Our redirect_to supports query string parameters, which\n# the normal Django generic one (irritatingly) does not.\ndef redirect_to(request, url):\n    query_string = request.META.get(\"QUERY_STRING\")\n    if query_string:\n        url = \"%s?%s\" % (url, query_string)\n    return HttpResponsePermanentRedirect(url)\n"
  },
  {
    "path": "dirigible/sheet/__init__.py",
    "content": ""
  },
  {
    "path": "dirigible/sheet/admin.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\r\n# See LICENSE.md\r\n#\r\nfrom .models import Sheet\r\nfrom django.contrib import admin\r\n\r\n\r\nclass SheetAdmin(admin.ModelAdmin):\r\n    list_display = ('id', 'owner', 'name')\r\n    list_filter = ('owner',)\r\n\r\nadmin.site.register(Sheet, SheetAdmin)\r\n\r\n"
  },
  {
    "path": "dirigible/sheet/calculate.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\nfrom __future__ import division\n\n\nimport json\nfrom Queue import Queue\nimport sys\nfrom threading import Thread\nfrom time import sleep, time\nimport traceback\nfrom urllib import urlencode\nimport urllib2\n\nfrom django.conf import settings\n\nfrom .cell import undefined\nfrom .dirigible_datetime import DateTime\nfrom .dependency_graph import build_dependency_graph\nfrom .eval_constant import eval_constant\nfrom .parser import FormulaError\nfrom .worksheet import CellRange, Worksheet\nfrom .utils.cell_name_utils import coordinates_to_cell_name\nfrom .utils.interruptable_thread import InterruptableThread\n\n\n# API version used by internal calls\nCURRENT_API_VERSION = '0.1'\nNUM_THREADS = 10\nINF = 1e9999\nNEG_INF = -INF\n\n\ndef is_nan(value):\n    return isinstance(value, float) and value != value\n\n\nclass MyStdout(object):\n    def write(self, text):\n        self.worksheet.add_console_text(text, log_type='output')\n\n    def __init__(self, worksheet):\n        self.worksheet = worksheet\n\n\ndef load_constants(worksheet):\n    for loc in worksheet.iterkeys():\n        formula = worksheet[loc].formula\n        if formula:\n            if not formula.startswith('='):\n                worksheet[loc].value = eval_constant(formula)\n                worksheet[loc].error = None\n\n\ndef set_cell_error_and_add_to_console(worksheet, location, exception):\n    cell = worksheet[location]\n    cell.value = undefined\n    cell.error = \"%s: %s\" % (exception.__class__.__name__, str(exception))\n    worksheet.add_console_text(\n        \"{error_text}\\n    Formula '{formula}' in {cell_name}\\n\".format(\n            error_text=cell.error,\n            formula=cell.formula,\n            cell_name=coordinates_to_cell_name(*location)\n        )\n    )\n\n\ndef recalculate_cell(location, leaf_queue, graph, context):\n    cell = context['worksheet'][location]\n    cell.error = None\n    try:\n        cell.value = eval(cell.python_formula, context)\n    except Exception, exc:\n        set_cell_error_and_add_to_console(context['worksheet'], location, exc)\n\n    graph[location].remove_from_parents(\n        [graph[parent_loc] for parent_loc in graph[location].parents],\n        leaf_queue\n    )\n\n\ndef create_cell_recalculator(leaf_queue, unrecalculated_queue, graph, context):\n    def cell_recalculator():\n        while not unrecalculated_queue.empty():\n            try:\n                leaf = leaf_queue.get(block=True, timeout=0.1)\n            except:\n                continue\n            try:\n                recalculate_cell(leaf, leaf_queue, graph, context)\n            finally:\n                leaf_queue.task_done()\n                unrecalculated_queue.get()\n                unrecalculated_queue.task_done()\n    return cell_recalculator\n\n\ndef evaluate_formulae_in_context(worksheet, context):\n    graph, leaves = build_dependency_graph(worksheet)\n    leaf_queue = Queue()\n    unrecalculated_queue = Queue()\n    for _ in graph:\n        unrecalculated_queue.put(1)\n\n    for _ in range(NUM_THREADS):\n        recalculator_thread = Thread(target=create_cell_recalculator(leaf_queue, unrecalculated_queue, graph, context))\n        recalculator_thread.setDaemon(True)\n        recalculator_thread.start()\n\n    for leaf in leaves:\n        leaf_queue.put(leaf)\n\n    unrecalculated_queue.join()\n\n\ndef execute_usercode(usercode, context):\n    exec(usercode, context)\n\n\ndef calculate_with_timeout(worksheet, usercode, timeout_seconds, private_key):\n    it = InterruptableThread(target=calculate, args=(worksheet, usercode, private_key))\n    it.start()\n    it.join(timeout_seconds)\n    while it.isAlive():\n        it.interrupt()\n        sleep(0.1)\n\n\ndef calculate(worksheet, usercode, private_key):\n    recalc_start = time()\n    _calculate(worksheet, usercode, private_key)\n    recalc_length = time() - recalc_start\n    worksheet.add_console_text('Took %.2fs' % (recalc_length,), log_type='system')\n\n\ndef _calculate(worksheet, usercode, private_key):\n    worksheet.clear_values()\n    worksheet._console_text = ''\n    worksheet._usercode_error = None\n\n    context = {\n        'worksheet': worksheet,\n        'load_constants': load_constants,\n        'undefined': undefined,\n\n        'CellRange': CellRange,\n        'DateTime': DateTime,\n        'FormulaError': FormulaError,\n        '_raise': _raise,\n        'sys': sys,\n    }\n    context['run_worksheet'] = lambda url, overrides=None: run_worksheet(url, overrides, private_key)\n    context['evaluate_formulae'] = lambda worksheet: evaluate_formulae_in_context(worksheet, context)\n    old_stdout = sys.stdout\n    sys.stdout = MyStdout(worksheet)\n\n    try:\n        execute_usercode(usercode, context)\n    except Exception as e:\n        if isinstance(e, SyntaxError):\n            error = 'Syntax error at character %d' % (e.offset,)\n            line_no = e.lineno\n            worksheet.add_console_text(\"%s (line %s)\\n\" % (error, line_no))\n        else:\n            error = \"%s: %s\" % (type(e).__name__, str(e))\n            tb = sys.exc_info()[2]\n            line_no = traceback.extract_tb(tb)[-1][1]\n            worksheet.add_console_text(\"%s\\n%s\\n\" % (error, format_traceback(traceback.extract_tb(tb))))\n        worksheet._usercode_error = {\"message\": error, \"line\": line_no}\n    finally:\n        sys.stdout = old_stdout\n\n\ndef format_traceback(frames):\n\n    def frame_is_visible_to_user(frame):\n        filename, _, function, __ = frame\n        return not filename.startswith(settings.BASE_DIR)\n\n\n    def format_frame(frame):\n        filename, line_no, function, source = frame\n        if filename == '<string>':\n            filename = '    User code'\n        else:\n            filename = '    File \"%s\"' % (filename,)\n        if function == '<module>':\n            function = ''\n        else:\n            function = ', in %s' % (function,)\n        if source is None:\n            source = ''\n        else:\n            source = '\\n        %s' % (source,)\n        return \"%s line %d%s%s\" % (filename, line_no, function, source)\n\n    return '\\n'.join(\n        map(\n            format_frame,\n            filter(frame_is_visible_to_user, frames)\n        )\n    )\n\n\ndef _raise(exc):\n    raise exc\n\n\ndef run_worksheet(worksheet_url, overrides, private_key):\n    target_url = '%sv%s/json/' % (worksheet_url, CURRENT_API_VERSION)\n    opener = urllib2.build_opener()\n    parameters = {}\n    parameters['dirigible_l337_private_key'] = private_key\n    if overrides is not None:\n        str_overrides = dict((loc, str(value)) for loc, value in overrides.iteritems())\n        parameters.update(str_overrides)\n    sheet_reader = opener.open(target_url, data=urlencode(parameters))\n    sheet = api_json_to_worksheet(sheet_reader.read())\n    if sheet._usercode_error:\n        raise Exception('run_worksheet: %s' % (sheet._usercode_error['message'],))\n    return sheet\n\n\ndef api_json_to_worksheet(sheet_json):\n    sheet_values = json.loads(sheet_json)\n    worksheet = Worksheet()\n\n    worksheet.name = sheet_values.get('name', 'Untitled')\n    for key, value in sheet_values.iteritems():\n        if key == \"usercode_error\":\n            worksheet._usercode_error = value\n        elif isinstance(value, dict):\n            rows = value\n            col = int(key)\n            for row, value in rows.iteritems():\n                row = int(row)\n                worksheet[col, row].value = value\n    return worksheet\n"
  },
  {
    "path": "dirigible/sheet/cell.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\nfrom .formula_interpreter import (\n        get_dependencies_from_parse_tree,\n        get_python_formula_from_parse_tree\n)\nfrom .parser import FormulaError, parser\n\n\n\nclass Undefined(object):\n\n    def __repr__(self):\n        return \"<undefined>\"\n\n\nundefined = Undefined()\n\n\nclass Cell(object):\n\n    def __init__(self):\n        self.clear()\n\n\n    def _set_formula(self, value):\n        self._python_formula = None\n        if value is None:\n            self._formula = None\n        elif type(value) == str or type(value) == unicode:\n            self._formula = value\n            if value.startswith('='):\n                try:\n                    parsed_formula = parser.parse(value)\n                    self.dependencies = get_dependencies_from_parse_tree(parsed_formula)\n                    self._python_formula = get_python_formula_from_parse_tree(parsed_formula)\n                except FormulaError, e:\n                    self.dependencies = []\n                    self._python_formula = '_raise(FormulaError(\"{}\"))'.format(e)\n        else:\n            raise TypeError('cell formula must be str or unicode')\n\n    def _get_formula(self):\n        return self._formula\n\n    formula = property(_get_formula, _set_formula)\n\n\n    def _set_python_formula(self, value):\n        if type(value) == str or type(value) == unicode:\n            self._python_formula = value\n        else:\n            raise TypeError('cell python_formula must be str or unicode')\n\n    def _get_python_formula(self):\n        return self._python_formula\n\n    python_formula = property(_get_python_formula, _set_python_formula)\n\n\n    def _set_value(self, value):\n        self._value = value\n        if value is undefined:\n            self._set_formatted_value(u'')\n        else:\n            self._set_formatted_value(unicode(value))\n\n    def _get_value(self):\n        return self._value\n\n    value = property(_get_value, _set_value)\n\n    def clear_value(self):\n        self._value = undefined\n\n\n    def _set_formatted_value(self, value):\n        if value is None:\n            self._formatted_value = u''\n        elif type(value) == str or type(value) == unicode:\n            self._formatted_value = value\n        else:\n            raise TypeError('cell formatted_value must be str or unicode')\n\n    def _get_formatted_value(self):\n        return self._formatted_value\n\n    formatted_value = property(_get_formatted_value, _set_formatted_value)\n\n\n    def clear(self):\n        self._value = undefined\n        self._formula = None\n        self._python_formula = None\n        self.dependencies = []\n        self._formatted_value = u''\n        self.error = None\n\n\n    def __repr__(self):\n        error = \"\"\n        if self.error:\n            error = \" error=%r\" % (self.error,)\n        return '<Cell formula=%s value=%r formatted_value=%r%s>' % \\\n            (self.formula, self._value, self.formatted_value, error)\n\n\n    def __eq__(self, other):\n        return (\n            isinstance(other, Cell) and\n            self._formula == other.formula and\n            self._value == other.value and\n            self._formatted_value == other.formatted_value and\n            self.error == other.error\n        )\n\n\n    def __ne__(self, other):\n        return not self.__eq__(other)\n"
  },
  {
    "path": "dirigible/sheet/cell_range.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\nfrom .utils.cell_name_utils import coordinates_to_cell_name\n\n\n\nclass CellRange(object):\n\n    def __init__(self, worksheet, start, end):\n        self.worksheet = worksheet\n        self.left, self.right = sorted([start[0], end[0]])\n        self.top, self.bottom =  sorted([start[1], end[1]])\n\n\n    def __repr__(self):\n        return '<CellRange %s to %s in %s>' % (\n            coordinates_to_cell_name(self.left, self.top),\n            coordinates_to_cell_name(self.right, self.bottom),\n            str(self.worksheet)\n        )\n\n\n    def __eq__(self, other):\n        return (\n            isinstance(other, CellRange) and\n            self.worksheet == other.worksheet and\n            self.left == other.left and\n            self.right == other.right and\n            self.top == other.top and\n            self.bottom == other.bottom\n        )\n\n\n    def __ne__(self, other):\n        return not self.__eq__(other)\n\n\n    def __len__(self):\n        return (self.right - self.left + 1) * (self.bottom - self.top + 1)\n\n\n    @property\n    def locations(self):\n        for r in xrange(self.top, self.bottom + 1):\n            for c in xrange(self.left, self.right + 1):\n                yield c, r\n\n\n    def __iter__(self):\n        for location in self.locations:\n            yield self.worksheet[location].value\n\n\n    @property\n    def cells(self):\n        for loc in self.locations:\n            yield self.worksheet[loc]\n\n\n    @property\n    def locations_and_cells(self):\n        for loc in self.locations:\n            yield loc, self.worksheet[loc]\n\n\n    def _location_in_worksheet(self, location):\n        if 0 in location:\n            raise IndexError('Cell ranges are 1-indexed')\n        num_cols = self.right - self.left + 1\n        num_rows = self.bottom - self.top + 1\n\n        if abs(location[0]) > num_cols:\n            raise IndexError('Cell range only has %s columns' % (num_cols, ))\n        elif abs(location[1]) > num_rows:\n            raise IndexError('Cell range only has %s rows' % (num_rows, ))\n        else:\n            return location[0] + self.left - 1, location[1] + self.top - 1\n\n\n    def __getitem__(self, location):\n            return self.worksheet[self._location_in_worksheet(location)]\n\n\n    def __setitem__(self, location, value):\n        self.worksheet[self._location_in_worksheet(location)] = value\n\n\n    def clear(self):\n        for cell in self.cells:\n            cell.clear()\n"
  },
  {
    "path": "dirigible/sheet/clipboard.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\n\nimport json\nimport StringIO\n\nfrom django.db import models\nfrom django.contrib.auth.models import User\n\nfrom .cell import Cell\nfrom .rewrite_formula_offset_cell_references import (\n    rewrite_formula, rewrite_source_sheet_formulae_for_cut\n)\nfrom .sheet import Sheet\nfrom .worksheet import (\n    CellRange, dump_cell_to_json_stream,\n)\n\n\nclass Clipboard(models.Model):\n    owner = models.ForeignKey(User)\n    contents_json = models.TextField(default='{}')\n    is_cut = models.BooleanField(default=False)\n    source_left = models.IntegerField(null=True)\n    source_top = models.IntegerField(null=True)\n    source_right = models.IntegerField(null=True)\n    source_bottom = models.IntegerField(null=True)\n    source_sheet = models.ForeignKey(Sheet, null=True)\n\n    @property\n    def source_range(self):\n        return (\n            self.source_left, self.source_top,\n            self.source_right, self.source_bottom\n        )\n\n    @property\n    def width(self):\n        return self.source_right - self.source_left + 1\n\n    @property\n    def height(self):\n        return self.source_bottom - self.source_top + 1\n\n\n    def copy(self, sheet, start, end):\n        self.is_cut = False\n        self.worksheet = sheet.unjsonify_worksheet()\n        self.cellrange = CellRange(self.worksheet, start, end)\n        cols_offset = self.cellrange.left\n        rows_offset = self.cellrange.top\n\n        stream = StringIO.StringIO()\n        stream.write(\"{ \")\n        first = True\n        for (col, row), cell in self.cellrange.locations_and_cells:\n            if not first:\n                stream.write(',')\n            first = False\n            if not cell.formula:\n                cell.formula = cell.formatted_value\n            dump_cell_to_json_stream(stream, col-cols_offset, row-rows_offset, cell)\n        stream.write(\" }\")\n\n        self.source_left = self.cellrange.left\n        self.source_right = self.cellrange.right\n        self.source_top = self.cellrange.top\n        self.source_bottom = self.cellrange.bottom\n\n        self.contents_json = stream.getvalue()\n        stream.close()\n\n\n    def cut(self, sheet, start, end):\n        self.copy(sheet, start, end)\n\n        for location in self.cellrange.locations:\n            if location in self.worksheet:\n                del self.worksheet[location]\n        sheet.jsonify_worksheet(self.worksheet)\n\n        self.is_cut = True\n        self.source_sheet = sheet\n\n\n    def _get_offset(self, col, row, start_col, start_row):\n        tile_offset_col = col // self.width * self.width\n        tile_offset_row = row // self.height * self.height\n        column_offset = start_col - self.source_left + tile_offset_col\n        row_offset = start_row - self.source_top + tile_offset_row\n        return column_offset, row_offset\n\n\n    def to_cells(self, start, end):\n        start_col, start_row = start\n        end_col, end_row = end\n\n        strings_dict = json.loads(self.contents_json)\n\n        for col in xrange(0, end_col - start_col + 1):\n            for row in xrange(0, end_row - start_row + 1):\n\n                clip_loc = col % self.width, row % self.height\n\n                clip_cell = strings_dict['%s,%s' % clip_loc]\n                dest_cell = Cell()\n                if clip_cell['formula']:\n                    column_offset, row_offset = self._get_offset(col, row, start_col, start_row)\n                    dest_cell.formula = rewrite_formula(\n                        clip_cell['formula'], column_offset, row_offset,\n                        self.is_cut, self.source_range\n                    )\n\n                dest_cell.formatted_value = clip_cell['formatted_value']\n                dest_loc = col + start_col, row + start_row\n                yield (dest_loc, dest_cell)\n\n\n    def paste_to(self, to_sheet, start, end):\n        start_col, start_row = start\n        if end == start:\n            end =  start_col + self.width - 1, start_row + self.height - 1\n\n        to_worksheet = to_sheet.unjsonify_worksheet()\n        if self.is_cut and to_sheet == self.source_sheet:\n            rewrite_source_sheet_formulae_for_cut(\n                to_worksheet,\n                self.source_range,\n                start_col, start_row\n            )\n\n        cells = self.to_cells(start, end)\n        for loc, cell in cells:\n            to_worksheet[loc] = cell\n\n        to_sheet.jsonify_worksheet(to_worksheet)\n\n        if self.is_cut:\n            self.source_left, self.source_top = start\n            self.source_right, self.source_bottom = end\n            self.is_cut = False\n            self.source_sheet = None\n\n"
  },
  {
    "path": "dirigible/sheet/dependency_graph.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\nfrom threading import Lock\n\nfrom .errors import report_cell_error, CycleError\n\n\nclass Node(object):\n    def __init__(self, location, children=None, parents=None):\n        self.location = location\n        self.children = children if children else set()\n        self.parents = parents if parents else set()\n        self.lock = Lock()\n\n    def __eq__(self, other):\n        return (\n            self.location == other.location and\n            self.children == other.children and\n            self.parents == other.parents\n        )\n\n    def __ne__(self, other):\n        return not self.__eq__(other)\n\n    def __repr__(self):\n        return \"<Node %d,%d children={%s} parents={%s}>\" % (\n            self.location[0], self.location[1],\n            ', '.join(str(i) for i in self.children),\n            ', '.join(str(i) for i in self.parents))\n\n    def remove_from_parents(self, parent_nodes, leaf_queue):\n        for parent in parent_nodes:\n            parent.lock.acquire()\n            parent.children.remove(self.location)\n            if len(parent.children) == 0:\n                leaf_queue.put(parent.location)\n            parent.lock.release()\n\n\n\n\ndef build_dependency_graph(worksheet):\n    graph = {}\n    visited = set()\n    for loc in worksheet.keys():\n        try:\n            _generate_cell_subgraph(worksheet, graph, loc, visited, [])\n        except CycleError:\n            pass # Deal with escapees\n\n    leaves = []\n    for loc, deps in graph.iteritems():\n        if not deps.children:\n            leaves.append(loc)\n\n    return graph, leaves\n\n\ndef _generate_cell_subgraph(worksheet, graph, loc, completed, path):\n    if loc not in worksheet:\n        return\n    cell = worksheet[loc]\n    if loc in completed:\n        if type(cell.error) == CycleError:\n            raise cell.error\n        else:\n            return\n\n    if loc in path:\n        cycle_error = CycleError(path[path.index(loc):] + [loc])\n        report_cell_error(worksheet, loc, cycle_error)\n        completed.add(loc)\n        raise cycle_error\n\n    if cell.python_formula:\n        valid_dependencies = set()\n        for dep_loc in cell.dependencies:\n            dep_cell = worksheet[dep_loc]\n            try:\n                _generate_cell_subgraph(worksheet, graph, dep_loc, completed, path + [loc])\n\n                if dep_cell.error:\n                    continue\n                if not dep_cell.python_formula:\n                    continue\n                valid_dependencies.add(dep_loc)\n            except CycleError as cycle_error:\n                if not loc in completed:\n                    report_cell_error(worksheet, loc, cycle_error)\n                if loc in cycle_error.path:\n                    completed.add(loc)\n                    raise cycle_error\n        _add_location_dependencies(graph, loc, valid_dependencies)\n    completed.add(loc)\n\n\ndef _add_location_dependencies(graph, location, dependencies):\n    if location not in graph:\n        graph[location] = Node(location)\n    graph[location].children |= dependencies\n    for dependency in dependencies:\n        if dependency not in graph:\n            graph[dependency] = Node(dependency)\n        graph[dependency].parents.add(location)\n\n\n"
  },
  {
    "path": "dirigible/sheet/dirigible_datetime.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\nimport datetime\n\nclass DateTime(datetime.datetime):\n    pass\n"
  },
  {
    "path": "dirigible/sheet/errors.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\nfrom .cell import undefined\nfrom .utils.cell_name_utils import coordinates_to_cell_name\n\n\nclass CycleError(Exception):\n\n    def __init__(self, path):\n        self.path = path\n\n\n    def __str__(self):\n        return ' -> '.join(coordinates_to_cell_name(*loc) for loc in self.path)\n\n\n    def __repr__(self):\n        return 'CycleError(%s)' % (str(self),)\n\n\n    def __eq__(self, other):\n        if isinstance(other, CycleError):\n            return self.path == other.path\n        return False\n\n\n    def __ne__(self, other):\n        return not self.__eq__(other)\n\n\ndef report_cell_error(worksheet, loc, exc):\n    worksheet[loc].value = undefined\n    worksheet[loc].error = \"%s: %s\" % (exc.__class__.__name__, str(exc))\n    worksheet.add_console_text(\"%s\\n    Formula '%s' in %s\\n\" % (\n    worksheet[loc].error, worksheet[loc].formula, coordinates_to_cell_name(*loc)))\n\n"
  },
  {
    "path": "dirigible/sheet/eval_constant.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\r\n# See LICENSE.md\r\n#\r\n\r\n\r\ndef eval_constant(constant):\r\n    try:\r\n        temp = float(constant)\r\n        try:\r\n            return int(constant)\r\n        except ValueError:\r\n            return temp\r\n    except ValueError:\r\n        return constant\r\n\r\n"
  },
  {
    "path": "dirigible/sheet/forms.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\nfrom django import forms\n\nclass ImportCSVForm(forms.Form):\n    def __init__(self, *args, **kwargs):\n        kwargs['auto_id'] = 'id_import_form_%s'\n        forms.Form.__init__(self, *args, **kwargs)\n    column = forms.IntegerField(widget=forms.widgets.HiddenInput)\n    row = forms.IntegerField(widget=forms.widgets.HiddenInput)\n    file = forms.FileField()\n\n"
  },
  {
    "path": "dirigible/sheet/formula_interpreter.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\nimport re\n\nfrom .parser import FormulaError\nfrom .parser.tokens import t_LITTLEARROW\nfrom .parser.parse_node import ParseNode\nfrom .parser.parse_node_constructors import (\n    Atom, ArgList, Name, Number, SubscriptList, Subscript, Trailer,\n)\n\nLITTLE_ARROW_RE = re.compile(t_LITTLEARROW)\n\n\ndef transform_arrow(child):\n    if isinstance(child, basestring) and LITTLE_ARROW_RE.match(child):\n        return ':'\n    else:\n        return child\n\n\ndef rewrite_cell_reference_as_tuple(cell_reference_node):\n    if cell_reference_node.type == ParseNode.FL_INVALID_REFERENCE:\n        raise FormulaError(\"#Invalid! cell reference in formula\")\n    if cell_reference_node.type == ParseNode.FL_DELETED_REFERENCE:\n        raise FormulaError(\"#Deleted! cell reference in formula\")\n\n    column, row = cell_reference_node.coords\n    return Atom([\n                 '(',\n                 Number(str(column)),\n                 ',',\n                 Number(str(row)),\n                 ')'\n                ])\n\n\ndef rewrite_cell_reference(cell_reference_node):\n    return Atom([\n        Name([\"worksheet\"]),\n        Trailer([\n            \"[\",\n            SubscriptList([\n                Subscript([ rewrite_cell_reference_as_tuple(cell_reference_node) ])\n            ]),\n            \"]\"\n            ]),\n        Trailer([\n            \".value\",\n        ]),\n        Name([\" \"])\n    ])\n\n\ndef rewrite_cell_range(node):\n    return Atom([\n        Name([\"CellRange\"]),\n        Trailer([\n            \"(\",\n            ArgList([\n                Name([\"worksheet\"]),\n                \",\",\n                rewrite_cell_reference_as_tuple(node.first_cell_reference),\n                \",\",\n                rewrite_cell_reference_as_tuple(node.second_cell_reference),\n            ]),\n            \")\",\n        ]),\n        Name([\" \"])\n    ])\n\n\nCELL_REFERENCES = (\n    ParseNode.FL_CELL_REFERENCE, ParseNode.FL_INVALID_REFERENCE,\n    ParseNode.FL_DELETED_REFERENCE\n)\nCONTAIN_COLONS = (\n    ParseNode.LAMBDEF, ParseNode.DICT_MAKER, ParseNode.SUBSCRIPT,\n    ParseNode.SLICE_OP\n)\n\ndef rewrite(parse_node):\n    if isinstance(parse_node, ParseNode):\n        if parse_node.type == ParseNode.FL_CELL_RANGE:\n            return rewrite_cell_range(parse_node)\n\n        elif parse_node.type in CELL_REFERENCES:\n            return rewrite_cell_reference(parse_node)\n\n        elif parse_node.type in CONTAIN_COLONS:\n            parse_node.children = map(\n                rewrite,\n                [transform_arrow(child) for child in parse_node.children]\n            )\n\n        else:\n            parse_node.children = map(rewrite, parse_node.children)\n\n    return parse_node\n\n\ndef get_python_formula_from_parse_tree(parse_node):\n    return rewrite(parse_node).flatten()[1:]\n\n\ndef get_dependencies_from_parse_tree(parse_node):\n    if not isinstance(parse_node, ParseNode):\n        return []\n\n    if parse_node.type == ParseNode.FL_CELL_REFERENCE:\n        return [parse_node.coords]\n\n    elif parse_node.type == ParseNode.FL_CELL_RANGE:\n        if parse_node.first_cell_reference.type in (ParseNode.FL_INVALID_REFERENCE, ParseNode.FL_DELETED_REFERENCE):\n            return []\n        if parse_node.second_cell_reference.type in (ParseNode.FL_INVALID_REFERENCE, ParseNode.FL_DELETED_REFERENCE):\n            return []\n        topleft = parse_node.first_cell_reference.coords\n        bottomright = parse_node.second_cell_reference.coords\n        left, right = sorted([topleft[0], bottomright[0]])\n        top, bottom = sorted([topleft[1], bottomright[1]])\n        return [\n            (c,r)\n            for c in xrange(left, right + 1)\n            for r in xrange(top, bottom + 1)\n        ]\n\n    elif parse_node.children:\n        return sum((get_dependencies_from_parse_tree(child) for child in parse_node.children), [])\n\n    else:\n        return []\n\n"
  },
  {
    "path": "dirigible/sheet/importer.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\nfrom chardet.universaldetector import UniversalDetector\nfrom codecs import getreader\nimport csv\nfrom xlrd import (\n    error_text_from_code, xldate_as_tuple, XL_CELL_DATE, XL_CELL_ERROR,\n)\nfrom worksheet import Worksheet\n\nclass DirigibleImportError(Exception):\n    pass\n\n\ndef worksheet_from_csv(\n    worksheet, csv_file, start_column, start_row, excel_encoding\n):\n\n    def autodetect_encoding(csv_file):\n        detector = UniversalDetector()\n        for line in csv_file.readlines():\n            detector.feed(line)\n            if detector.done: break\n        detector.close()\n        csv_file.seek(0)\n        encoding = detector.result['encoding']\n\n        if not encoding:\n            raise DirigibleImportError('could not recognise encoding')\n\n        return encoding\n\n    def unicode_csv_reader(unicode_csv_data, dialect=csv.excel, **kwargs):\n        # csv.py doesn't do Unicode; encode temporarily as UTF-8:\n        csv_reader = csv.reader(utf_8_encoder(unicode_csv_data),\n                                dialect=dialect, **kwargs)\n        for row in csv_reader:\n            # decode UTF-8 back to Unicode, cell by cell:\n            yield [unicode(cell, 'utf-8') for cell in row]\n\n    def utf_8_encoder(unicode_csv_data):\n        for line in unicode_csv_data:\n            yield line.encode('utf-8')\n\n\n    if excel_encoding:\n        encoding = 'windows-1252'\n    else:\n        encoding = autodetect_encoding(csv_file)\n    unicode_translated_csv_file = getreader(encoding)(csv_file)\n\n    row = start_row\n    try:\n        for csv_row in unicode_csv_reader(unicode_translated_csv_file):\n            column = start_column\n            for csv_cell in csv_row:\n                worksheet[column, row].formula = unicode(csv_cell)\n                column += 1\n            row += 1\n    except Exception, e:\n        raise DirigibleImportError(unicode(e))\n\n    return worksheet\n\n\ndef worksheet_from_excel(excel_sheet):\n    worksheet = Worksheet()\n    for col in range(excel_sheet.ncols):\n        for row in range(excel_sheet.nrows):\n            cell = excel_sheet.cell(row, col)\n            if cell.ctype == XL_CELL_ERROR:\n                formula = '=%s' % (error_text_from_code[cell.value], )\n            elif cell.ctype == XL_CELL_DATE:\n                formula = '=DateTime(%s, %s, %s, %s, %s, %s)' % xldate_as_tuple(\n                    cell.value, excel_sheet.book.datemode)\n            else:\n                formula = unicode(excel_sheet.cell(row, col).value)\n            worksheet[col + 1, row + 1].formula = formula\n    return worksheet\n\n"
  },
  {
    "path": "dirigible/sheet/migrations/0001_initial.py",
    "content": "# -*- coding: utf-8 -*-\nfrom __future__ import unicode_literals\n\nfrom django.db import models, migrations\nfrom django.conf import settings\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        migrations.swappable_dependency(settings.AUTH_USER_MODEL),\n    ]\n\n    operations = [\n        migrations.CreateModel(\n            name='Clipboard',\n            fields=[\n                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),\n                ('contents_json', models.TextField(default=b'{}')),\n                ('is_cut', models.BooleanField(default=False)),\n                ('source_left', models.IntegerField(null=True)),\n                ('source_top', models.IntegerField(null=True)),\n                ('source_right', models.IntegerField(null=True)),\n                ('source_bottom', models.IntegerField(null=True)),\n                ('owner', models.ForeignKey(to=settings.AUTH_USER_MODEL)),\n            ],\n            options={\n            },\n            bases=(models.Model,),\n        ),\n        migrations.CreateModel(\n            name='Sheet',\n            fields=[\n                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),\n                ('last_modified', models.DateTimeField(auto_now=True)),\n                ('version', models.IntegerField(default=0)),\n                ('name', models.TextField(default=b'Untitled')),\n                ('width', models.IntegerField(default=52)),\n                ('height', models.IntegerField(default=1000)),\n                ('contents_json', models.TextField(default=b'{ \"_console_text\": \"\", \"_usercode_error\": null  }')),\n                ('timeout_seconds', models.IntegerField(default=55)),\n                ('is_public', models.BooleanField(default=False)),\n                ('allow_json_api_access', models.BooleanField(default=False)),\n                ('api_key', models.CharField(max_length=72)),\n                ('column_widths_json', models.TextField(default=b'{}')),\n                ('usercode', models.TextField(default=b'\\nload_constants(worksheet)\\n\\n# Put code here if it needs to access constants in the spreadsheet\\n# and to be accessed by the formulae.  Examples: imports,\\n# user-defined functions and classes you want to use in cells.\\n\\nevaluate_formulae(worksheet)\\n\\n# Put code here if it needs to access the results of the formulae.\\n')),\n                ('owner', models.ForeignKey(to=settings.AUTH_USER_MODEL)),\n            ],\n            options={\n            },\n            bases=(models.Model,),\n        ),\n        migrations.AddField(\n            model_name='clipboard',\n            name='source_sheet',\n            field=models.ForeignKey(to='sheet.Sheet', null=True),\n            preserve_default=True,\n        ),\n    ]\n"
  },
  {
    "path": "dirigible/sheet/migrations/__init__.py",
    "content": ""
  },
  {
    "path": "dirigible/sheet/models.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\nfrom .clipboard import Clipboard\nfrom .sheet import Sheet\nfrom django.contrib.auth.models import User\n\n\n\ndef copy_sheet_to_user(sheet, user):\n    sheet.id = None\n    sheet.owner = user\n    sheet.is_public = False\n    sheet.save()\n    return sheet\n\n"
  },
  {
    "path": "dirigible/sheet/parser/__init__.py",
    "content": "class FormulaError(Exception):\r\n    \"\"\"\r\n    This exception is used to indicate that a cell expression is badly formed.\r\n    It is the equivalent of a ``SyntaxError`` in normal Python code.\r\n    \"\"\""
  },
  {
    "path": "dirigible/sheet/parser/fl_cell_range_parse_node.py",
    "content": "# Copyright (c) 2005-2009 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\nfrom sheet.parser.parse_node import ParseNode\n\nclass FLCellRangeParseNode(ParseNode):\n\n    def __init__(self, children):\n        assert len(children) == 3\n        ParseNode.__init__(self, ParseNode.FL_CELL_RANGE, children)\n\n    def __get_first_cell_reference(self):\n        return self.children[0]\n    def __set_first_cell_reference(self, cellRef):\n        self.children[0] = cellRef\n\n    first_cell_reference = property(__get_first_cell_reference, __set_first_cell_reference)\n\n    def __get_second_cell_reference(self):\n        return self.children[2]\n\n    def __set_second_cell_reference(self, cellRef):\n        self.children[2] = cellRef\n\n    second_cell_reference = property(__get_second_cell_reference, __set_second_cell_reference)\n\n    @property\n    def colon(self):\n        return self.children[1]\n\nParseNode.register_node_type(ParseNode.FL_CELL_RANGE, FLCellRangeParseNode)\n"
  },
  {
    "path": "dirigible/sheet/parser/fl_cell_reference_parse_node.py",
    "content": "# Copyright (c) 2005-2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\nfrom sheet.parser.parse_node import ParseNode\nfrom sheet.parser.fl_reference_parse_node import FLReferenceParseNode\nfrom sheet.utils.cell_name_utils import cell_name_to_coordinates, coordinates_to_cell_name\n\nclass FLCellReferenceParseNode(FLReferenceParseNode):\n\n    def __init__(self, children):\n        FLReferenceParseNode.__init__(self, ParseNode.FL_CELL_REFERENCE, children)\n\n\n    @property\n    def colAbsolute(self):\n        return self.localReference.startswith(\"$\")\n\n\n    @property\n    def rowAbsolute(self):\n        return '$' in self.localReference[1:]\n\n\n    @property\n    def plainCellName(self):\n        return self.localReference.replace('$', '')\n\n\n    def __getLocalReference(self):\n        return self.children[-1]\n\n    def __setLocalReference(self, cellRef):\n        self.children[-1] = cellRef\n\n    localReference = property(__getLocalReference, __setLocalReference)\n\n\n    @property\n    def coords(self):\n        return cell_name_to_coordinates(self.plainCellName)\n\n\n    def offset(self, dx, dy, move_absolute=False):\n        (col, row) = cell_name_to_coordinates(self.plainCellName)\n\n        if move_absolute or not self.colAbsolute:\n            col += dx\n        if move_absolute or not self.rowAbsolute:\n            row += dy\n\n        newName = coordinates_to_cell_name(col, row, colAbsolute=self.colAbsolute, rowAbsolute=self.rowAbsolute)\n        if newName is None:\n            newName = \"#Invalid!\"\n\n        self.localReference = newName + self.whitespace\n\n\n    def canonicalise(self, wsNames):\n        self.localReference = self.localReference.upper()\n        FLReferenceParseNode.canonicalise(self, wsNames)\n\n\nParseNode.register_node_type(ParseNode.FL_CELL_REFERENCE, FLCellReferenceParseNode)\n"
  },
  {
    "path": "dirigible/sheet/parser/fl_column_reference_parse_node.py",
    "content": "# Copyright (c) 2005-2009 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\nfrom sheet.parser.parse_node import ParseNode\nfrom sheet.parser.fl_reference_parse_node import FLReferenceParseNode\nfrom sheet.utils.cell_name_utils import column_name_to_index, column_index_to_name\n\n\nclass FLColumnReferenceParseNode(FLReferenceParseNode):\n\n    def __init__(self, children):\n        FLReferenceParseNode.__init__(self, ParseNode.FL_COLUMN_REFERENCE, children)\n\n\n    @property\n    def isAbsolute(self):\n        return self.localReference.lstrip().startswith(\"$\")\n\n\n    def __getPlainColumnName(self):\n        return self.localReference.strip().replace('$', '').replace('_', '')\n    def __setPlainColumnName(self, newName):\n        self.children[-1] = self.localReference.replace(self.plainColumnName, newName)\n    plainColumnName = property(__getPlainColumnName, __setPlainColumnName)\n\n\n    @property\n    def colIndex(self):\n        return column_name_to_index(self.plainColumnName)\n\n    @property\n    def coords(self):\n        return self.colIndex, 0\n\n\n    def offset(self, count, _, moveAbsolute=False):\n        if not moveAbsolute and self.isAbsolute:\n            return\n        newName = column_index_to_name(self.colIndex + count)\n        if newName:\n            self.plainColumnName = newName\n        else:\n            self.localReference = '#Invalid!' + self.whitespace\n\n    def __getLocalReference(self):\n        return self.children[-1]\n    def __setLocalReference(self, newCol):\n        self.children[-1] = newCol\n\n    localReference = property(__getLocalReference, __setLocalReference)\n\n\n    def canonicalise(self, wsNames):\n        self.localReference = self.localReference.upper()\n        FLReferenceParseNode.canonicalise(self, wsNames)\n\nParseNode.register_node_type(ParseNode.FL_COLUMN_REFERENCE, FLColumnReferenceParseNode)\n"
  },
  {
    "path": "dirigible/sheet/parser/fl_named_column_reference_parse_node.py",
    "content": "# Copyright (c) 2005-2009 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\nfrom sheet.parser.parse_node import ParseNode\nfrom sheet.parser.fl_reference_parse_node import FLReferenceParseNode\n\n\nclass FLNamedColumnReferenceParseNode(FLReferenceParseNode):\n\n    def __init__(self, children):\n        FLReferenceParseNode.__init__(self, ParseNode.FL_NAMED_COLUMN_REFERENCE, children)\n\n\n    @property\n    def header(self):\n        return self.children[-1].rstrip().replace(\"##\", \"#\")[1:-2]\n\n\nParseNode.register_node_type(ParseNode.FL_NAMED_COLUMN_REFERENCE, FLNamedColumnReferenceParseNode)\n"
  },
  {
    "path": "dirigible/sheet/parser/fl_named_row_reference_parse_node.py",
    "content": "# Copyright (c) 2005-2009 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\nfrom sheet.parser.parse_node import ParseNode\nfrom sheet.parser.fl_reference_parse_node import FLReferenceParseNode\n\n\nclass FLNamedRowReferenceParseNode(FLReferenceParseNode):\n\n    def __init__(self, children):\n        FLReferenceParseNode.__init__(self, ParseNode.FL_NAMED_ROW_REFERENCE, children)\n\n\n    @property\n    def header(self):\n        return self.children[-1].rstrip().replace(\"##\", \"#\")[2:-1]\n\n\nParseNode.register_node_type(ParseNode.FL_NAMED_ROW_REFERENCE, FLNamedRowReferenceParseNode)\n"
  },
  {
    "path": "dirigible/sheet/parser/fl_reference_parse_node.py",
    "content": "# Copyright (c) 2005-2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\nimport re\n\nfrom sheet.parser.parse_node import ParseNode\nfrom sheet.utils.string_utils import correct_case, get_rstripped_part\n\n\n_worksheetNameRegex = re.compile(r\"^[A-Za-z]\\w*$\")\ndef quote_fl_worksheet_name(name):\n    if re.match(_worksheetNameRegex, name) is not None:\n        return name\n    return \"'%s'\" % name.replace(\"'\", \"''\")\n\n\ndef unquote_fl_worksheet_name(name):\n    name = name.replace(\"''\", \"'\")\n    if name.startswith(\"'\"):\n        return name[1:-1]\n    return name\n\n\nclass FLReferenceParseNode(ParseNode):\n\n    def __init__(self, nodeType, children):\n        assert len(children) in (1, 3)\n        ParseNode.__init__(self, nodeType, children)\n\n\n    @property\n    def whitespace(self):\n        return get_rstripped_part(self.children[-1])\n\n\n    def __getWorksheet(self):\n        if len(self.children) == 3:\n            return unquote_fl_worksheet_name(self.children[0].rstrip())\n        return None\n\n    def __setWorksheet(self, ws):\n        if ws is None:\n            self.children = [self.children[-1]]\n            return\n\n        ws = quote_fl_worksheet_name(ws)\n        if self.worksheetReference:\n            self.children[0] = ws + get_rstripped_part(self.children[0])\n        else:\n            self.children = [ws, '!', self.children[-1]]\n\n    worksheetReference = property(__getWorksheet, __setWorksheet)\n\n\n    def canonicalise(self, wsNames):\n        if self.worksheetReference:\n            self.worksheetReference = correct_case(self.worksheetReference, wsNames)\n"
  },
  {
    "path": "dirigible/sheet/parser/fl_row_reference_parse_node.py",
    "content": "# Copyright (c) 2005-2009 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\nfrom sheet.parser.parse_node import ParseNode\nfrom sheet.parser.fl_reference_parse_node import FLReferenceParseNode\n\nclass FLRowReferenceParseNode(FLReferenceParseNode):\n\n    def __init__(self, children):\n        ParseNode.__init__(self, ParseNode.FL_ROW_REFERENCE, children)\n\n    @property\n    def isAbsolute(self):\n        return '$' in self.children[-1]\n\n    def __getPlainRowName(self):\n        return self.children[-1][1:].strip().replace('$', '')\n    def __setPlainRowName(self, newName):\n        self.children[-1] = self.localReference.replace(self.plainRowName, newName)\n    plainRowName = property(__getPlainRowName, __setPlainRowName)\n\n    @property\n    def rowIndex(self):\n        return int(self.plainRowName)\n\n\n    @property\n    def coords(self):\n        return 0, self.rowIndex\n\n\n    def offset(self, _, count, moveAbsolute=False):\n        if not moveAbsolute and self.isAbsolute:\n            return\n        newIndex = self.rowIndex + count\n        if newIndex > 0:\n            self.plainRowName = str(newIndex)\n        else:\n            self.localReference = '#Invalid!' + self.whitespace\n\n\n    def __getLocalReference(self):\n        return self.children[-1]\n    def __setLocalReference(self, newRow):\n        self.children[-1] = newRow\n    localReference = property(__getLocalReference, __setLocalReference)\n\n\nParseNode.register_node_type(ParseNode.FL_ROW_REFERENCE, FLRowReferenceParseNode)\n\n"
  },
  {
    "path": "dirigible/sheet/parser/grammar.py",
    "content": "# Copyright (c) 2005-2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\nimport sys\n\nfrom sheet.utils.cell_name_utils import cell_name_to_coordinates, column_name_to_index\nfrom sheet.utils.string_utils import get_lstripped_part, get_rstripped_part\n\nfrom sheet.parser import FormulaError\nfrom sheet.parser.parse_node import ParseNode\nfrom sheet.parser.parse_node_constructors import (\n    AndTest,\n    ArgList,\n    Argument,\n    ArithExpr,\n    Atom,\n    Comparison,\n    CompOperator,\n    ConcatExpr,\n    DictMaker,\n    Expr,\n    ExprList,\n    Factor,\n    Percent,\n    FLCellRange,\n    FLCellReference,\n    FLColumnReference,\n    FLRowReference,\n    FLDDECall,\n    FLDeletedReference,\n    FLInvalidReference,\n    FLNakedWorksheetReference,\n    FLNamedColumnReference,\n    FLNamedRowReference,\n    FLReference,\n    FLRoot,\n    FPDef,\n    FPList,\n    GenFor,\n    GenIf,\n    GenIter,\n    LambDef,\n    ListFor,\n    ListIf,\n    ListIter,\n    ListMaker,\n    Name,\n    NotTest,\n    Number,\n    Power,\n    ShiftExpr,\n    SliceOp,\n    StringLiteral,\n    Subscript,\n    SubscriptList,\n    Term,\n    Test,\n    TestFromAtomChild,\n    TestList,\n    TestListGexp,\n    Trailer,\n    VarArgsList,\n)\n\nfrom sheet.parser.tokens import DOT_RE, FLCELLREFLIKENAME_RE, tokens\n\n\n# Rule to allow us to have the rest of the grammar in\n# the order in which it is defined in the Python 2.4.3\n# codebase.\ndef p__root(p):\n    \"\"\"_root : EQUALS test\"\"\"\n    p[0] = FLRoot([p[1], p[2]])\n\n\n# The remainder of this file comprises the rules required to\n# define a test plus a number of \"virtual\" rules to make the\n# parser's job (and ours) easier.  NB these latter (like _root\n# above) have underscores before their names to make them\n# easily identifiable.\n\ndef p_varargslist(p):\n    \"\"\"varargslist : _partial_varargslist_starting_with_fpdef\n                   | _partial_varargslist_starting_with_keyword\n                   | _partial_varargslist_starting_with_star\n                   | _partial_varargslist_starting_with_doublestar\"\"\"\n    p[0] = VarArgsList(p[1])\n\n\ndef p__partial_varargslist_starting_with_doublestar(p):\n    \"\"\"_partial_varargslist_starting_with_doublestar : DOUBLESTAR _name\"\"\"\n    p[0] = p[1:]\n\n\ndef p__partial_varargslist_starting_with_star(p):\n    \"\"\"_partial_varargslist_starting_with_star : STAR _name\n                                               | STAR _name COMMA _partial_varargslist_starting_with_doublestar\"\"\"\n    if len(p) == 3:\n        p[0] = [p[1], p[2]]\n    else:\n        p[0] = [p[1], p[2], p[3]] + p[4]\n\n\ndef p__partial_varargslist_starting_with_keyword(p):\n    \"\"\"_partial_varargslist_starting_with_keyword : fpdef COLONEQUALS test\n                                                  | fpdef COLONEQUALS test COMMA\n                                                  | fpdef COLONEQUALS test COMMA _partial_varargslist_starting_with_doublestar\n                                                  | fpdef COLONEQUALS test COMMA _partial_varargslist_starting_with_star\"\"\"\n    if len(p) < 6:\n        p[0] = p[1:]\n    else:\n        p[0] = [p[1], p[2], p[3], p[4]] + p[5]\n\n\ndef p__partial_varargslist_starting_with_fpdef(p):\n    \"\"\"_partial_varargslist_starting_with_fpdef : fpdef\n                                                | fpdef COMMA\n                                                | fpdef COMMA _partial_varargslist_starting_with_doublestar\n                                                | fpdef COMMA _partial_varargslist_starting_with_star\n                                                | fpdef COMMA _partial_varargslist_starting_with_keyword\n                                                | fpdef COMMA _partial_varargslist_starting_with_fpdef\"\"\"\n    if len(p) < 4:\n        p[0] = p[1:]\n    else:\n        p[0] = [p[1], p[2]] + p[3]\n\n\ndef p_fpdef(p):\n    \"\"\"fpdef : _name\n             | LEFTPAREN fplist RIGHTPAREN\"\"\"\n\n    p[0] = FPDef(p[1:])\n\n\ndef p_fplist(p):\n    \"\"\"fplist : fpdef\n              | fpdef COMMA\n              | fpdef _comma_fpdefs\n              | fpdef _comma_fpdefs COMMA\"\"\"\n    if len(p) == 2:\n        p[0] = FPList([p[1]])\n    elif len(p) == 3:\n        if str(p[2]).rstrip() == \",\":\n            p[0] = FPList([p[1], p[2]])\n        else:\n            p[0] = FPList([p[1]] + p[2])\n    else:\n        p[0] = FPList([[p[1]] + p[2] + [p[3]]])\n\n\ndef p__comma_fpdefs(p):\n    \"\"\"_comma_fpdefs : COMMA fpdef\n                     | _comma_fpdefs COMMA fpdef\"\"\"\n    if len(p) == 3:\n        p[0] = [p[1], p[2]]\n    else:\n        p[0] = p[1] + [p[2], p[3]]\n\n\ndef p_test(p):\n    \"\"\"test : and_test\n            | and_test _or_and_tests\n            | lambdef\"\"\"\n    if len(p) == 2:\n        p[0] = Test([p[1]])\n    else:\n        p[0] = Test([p[1]] + p[2])\n\n\ndef p__or_and_tests(p):\n    \"\"\"_or_and_tests : OR and_test\n                     | _or_and_tests OR and_test\"\"\"\n    if len(p) == 3:\n        p[0] = [p[1], p[2]]\n    else:\n        p[0] = p[1] + [p[2], p[3]]\n\n\ndef p_and_test(p):\n    \"\"\"and_test : not_test\n                | not_test _and_not_tests\"\"\"\n    if len(p) == 2:\n        p[0] = AndTest([p[1]])\n    else:\n        p[0] = AndTest([p[1]] + p[2])\n\n\ndef p__and_not_tests(p):\n    \"\"\"_and_not_tests : AND not_test\n                      | _and_not_tests AND not_test\"\"\"\n    if len(p) == 3:\n        p[0] = [p[1], p[2]]\n    else:\n        p[0] = p[1] + [p[2], p[3]]\n\n\ndef p_not_test(p):\n    \"\"\"not_test : comparison\n                | NOT not_test\"\"\"\n    if len(p) == 2:\n        p[0] = NotTest([p[1]])\n    else:\n        p[0] = NotTest([p[1], p[2]])\n\n\ndef p_comparison(p):\n    \"\"\"comparison : expr\n                  | expr _comp_operator_exprs\"\"\"\n    if len(p) == 2:\n        p[0] = Comparison([p[1]])\n    else:\n        p[0] = Comparison([p[1]] + p[2])\n\n\ndef p__comp_operator_exprs(p):\n    \"\"\"_comp_operator_exprs : comp_operator expr\n                            | _comp_operator_exprs comp_operator expr\"\"\"\n    if len(p) == 3:\n        p[0] = [p[1], p[2]]\n    else:\n        p[0] = p[1] + [p[2], p[3]]\n\n\ndef p_comp_operator(p):\n    \"\"\"comp_operator : LESSTHAN\n                     | GREATERTHAN\n                     | EQUALS\n                     | EQUALTO\n                     | GREATEREQUAL\n                     | LESSEQUAL\n                     | UNEQUAL\n                     | OBSOLETEUNEQUAL\n                     | IS\n                     | IN\n                     | IS NOT\n                     | NOT IN\"\"\"\n    if len(p) == 2:\n        p[0] = CompOperator([p[1]])\n    else:\n        p[0] = CompOperator([p[1] + p[2]])\n\n\ndef p_expr(p):\n    \"\"\"expr : concat_expr\n            | concat_expr _or_concat_exprs\"\"\"\n    if len(p) == 2:\n        p[0] = Expr([p[1]])\n    else:\n        p[0] = Expr([p[1]] + p[2])\n\n\ndef p__or_concat_exprs(p):\n    \"\"\"_or_concat_exprs : VERTICALBAR concat_expr\n                     | _or_concat_exprs VERTICALBAR concat_expr\"\"\"\n    if len(p) == 3:\n        p[0] = [p[1], p[2]]\n    else:\n        p[0] = p[1] + [p[2], p[3]]\n\n\ndef p_concat_expr(p):\n    \"\"\"concat_expr : shift_expr\n                | shift_expr _ampersand_shift_exprs\"\"\"\n    if len(p) == 2:\n        p[0] = ConcatExpr([p[1]])\n    else:\n        p[0] = ConcatExpr([p[1]] + p[2])\n\n\ndef p__ampersand_shift_exprs(p):\n    \"\"\"_ampersand_shift_exprs : AMPERSAND shift_expr\n                              | _ampersand_shift_exprs AMPERSAND shift_expr\"\"\"\n    if len(p) == 3:\n        p[0] = [p[1], p[2]]\n    else:\n        p[0] = p[1] + [p[2], p[3]]\n\n\ndef p_shift_expr(p):\n    \"\"\"shift_expr : arith_expr\n                  | arith_expr _shift_op_arith_exprs\"\"\"\n    if len(p) == 2:\n        p[0] = ShiftExpr([p[1]])\n    else:\n        p[0] = ShiftExpr([p[1]]+ p[2])\n\n\ndef p__shift_op_arith_exprs(p):\n    \"\"\"_shift_op_arith_exprs : LEFTSHIFT arith_expr\n                             | RIGHTSHIFT arith_expr\n                             | _shift_op_arith_exprs LEFTSHIFT arith_expr\n                             | _shift_op_arith_exprs RIGHTSHIFT arith_expr\"\"\"\n    if len(p) == 3:\n        p[0] = [p[1], p[2]]\n    else:\n        p[0] = p[1] + [p[2], p[3]]\n\n\ndef p_arith_expr(p):\n    \"\"\"arith_expr : term\n                  | term _a_op_terms\"\"\"\n    if len(p) == 2:\n        p[0] = ArithExpr([p[1]])\n    else:\n        p[0] = ArithExpr([p[1]] + p[2])\n\n\ndef p__a_op_terms(p):\n    \"\"\"_a_op_terms : PLUS term\n                   | MINUS term\n                   | _a_op_terms PLUS term\n                   | _a_op_terms MINUS term\"\"\"\n    if len(p) == 3:\n        p[0] = [p[1], p[2]]\n    else:\n        p[0] = p[1] + [p[2], p[3]]\n\n\ndef p_term(p):\n    \"\"\"term : percent\n            | percent _m_op_factors\"\"\"\n    if len(p) == 2:\n        p[0] = Term([p[1]])\n    else:\n        p[0] = Term([p[1]] + p[2])\n\n\ndef p__m_op_factors(p):\n    \"\"\"_m_op_factors : STAR percent\n                      | SLASH percent\n                      | DOUBLESLASH percent\n                      | MOD_ITERP percent\n                      | _m_op_factors STAR percent\n                      | _m_op_factors SLASH percent\n                      | _m_op_factors DOUBLESLASH percent\n                      | _m_op_factors MOD_ITERP percent\"\"\"\n    if len(p) == 3:\n        p[0] = [p[1], p[2]]\n    else:\n        p[0] = p[1] + [p[2], p[3]]\n\ndef p_percent(p):\n    \"\"\"percent : factor\n               | percent PERCENT\"\"\"\n    if len(p) == 2:\n        p[0] = p[1]\n    else:\n        p[0] = Percent(p[1:])\n\ndef p_factor(p):\n    \"\"\"factor : power\n              | MINUS factor\n              | PLUS factor\n              | TILDE factor\"\"\"\n    p[0] = Factor(p[1:])\n\n\ndef p_power(p):\n    \"\"\"power : fl_reference\n             | fl_reference DOUBLESTAR factor\n             | fl_reference CIRCUMFLEX factor\"\"\"\n    if len(p) == 2:\n        p[0] = Power([p[1]])\n    else:\n        p[0] = Power([p[1], p[2], p[3]])\n\n\ndef p_atom(p):\n    \"\"\"atom : _number\n            | stringliteral\n            | _name\n            | BACKWARDQUOTE testlist1 BACKWARDQUOTE\n            | LEFTPAREN RIGHTPAREN\n            | LEFTPAREN testlist_gexp RIGHTPAREN\n            | LEFTBRACKET RIGHTBRACKET\n            | LEFTBRACKET listmaker RIGHTBRACKET\n            | LEFTBRACE RIGHTBRACE\n            | LEFTBRACE dictmaker RIGHTBRACE\"\"\"\n    p[0] = Atom(p[1:])\n\n\ndef p_listmaker(p):\n    \"\"\"listmaker : testlist\n                 | test list_for\"\"\"\n    if len(p) == 2:\n        p[0] = ListMaker(p[1].children)\n    else:\n        p[0] = ListMaker([p[1], p[2]])\n\n\ndef p_testlist_gexp(p):\n    \"\"\"testlist_gexp : testlist\n                     | test gen_for\"\"\"\n    if len(p) == 2:\n        p[0] = TestListGexp(p[1].children)\n    else:\n        p[0] = TestListGexp([p[1], p[2]])\n\n\ndef p_lambdef(p):\n    \"\"\"lambdef : LAMBDA LITTLEARROW test\n               | LAMBDA varargslist LITTLEARROW test\"\"\"\n    p[0] = LambDef(p[1:])\n\n\ndef p_trailer(p):\n    \"\"\"trailer : DOT _name\n               | DOT FLCELLREFLIKENAME\n               | LEFTPAREN RIGHTPAREN\n               | LEFTPAREN arglist RIGHTPAREN\n               | LEFTBRACKET subscriptlist RIGHTBRACKET\"\"\"\n\n    def __ConvertCellRefLikeNameToNameNode(p):\n        if len(p) == 3 and DOT_RE.match(p[1]) and isinstance(p[2], basestring) and FLCELLREFLIKENAME_RE.match(p[2]):\n            return Name([p[2]])\n        else:\n            return p[2]\n\n    p[2] = __ConvertCellRefLikeNameToNameNode(p)\n    p[0] = Trailer(p[1:])\n\n\ndef p__trailers(p):\n    \"\"\"_trailers : trailer\n                 | _trailers trailer\"\"\"\n    if len(p) == 2:\n        p[0] = [p[1]]\n    else:\n        p[0] = p[1] + [p[2]]\n\n\ndef p_subscriptlist(p):\n    \"\"\"subscriptlist : subscript\n                     | subscript COMMA\n                     | subscript _comma_subscripts\n                     | subscript _comma_subscripts COMMA\"\"\"\n    if len(p) == 2:\n        p[0] = SubscriptList([p[1]])\n    elif len(p) == 3:\n        if str(p[2]).rstrip() == \",\":\n            p[0] = SubscriptList([p[1], p[2]])\n        else:\n            p[0] = SubscriptList([p[1]] + p[2])\n    else:\n        p[0] = SubscriptList([p[1]] + p[2] + [p[3]])\n\n\ndef p__comma_subscripts(p):\n    \"\"\"_comma_subscripts : COMMA subscript\n                         | _comma_subscripts COMMA subscript\"\"\"\n    if len(p) == 3:\n        p[0] = [p[1], p[2]]\n    else:\n        p[0] = p[1] + [p[2], p[3]]\n\n\ndef p_subscript(p):\n    \"\"\"subscript : _simple_subscript\n                 | _complex_subscript\"\"\"\n    p[0] = p[1]\n\n\ndef p__simple_subscript(p):\n    \"\"\"_simple_subscript : ELLIPSIS\n                         | test\"\"\"\n    p[0] = Subscript([p[1]])\n\n\ndef p__complex_subscript(p):\n    \"\"\"_complex_subscript : _short_slice\n                          | _short_slice sliceop\"\"\"\n    if len(p) == 2:\n        p[0] = Subscript(p[1])\n    else:\n        p[0] = Subscript(p[1] + [p[2]])\n\n\ndef p__short_slice(p):\n    \"\"\"_short_slice : LITTLEARROW\n                    | _lower_bound LITTLEARROW\n                    | LITTLEARROW _upper_bound\n                    | _lower_bound LITTLEARROW _upper_bound\"\"\"\n    p[0] = p[1:]\n\n\ndef p__lower_bound(p):\n    \"\"\"_lower_bound : test\"\"\"\n    p[0] = p[1]\n\n\ndef p__upper_bound(p):\n    \"\"\"_upper_bound : test\"\"\"\n    p[0] = p[1]\n\n\ndef p_sliceop(p):\n    \"\"\"sliceop : LITTLEARROW\n               | LITTLEARROW test\"\"\"\n    p[0] = SliceOp(p[1:])\n\n\ndef p_exprlist(p):\n    \"\"\"exprlist : expr\n                | expr COMMA\n                | expr _comma_exprs\n                | expr _comma_exprs COMMA\"\"\"\n    if len(p) == 2:\n        p[0] = ExprList([p[1]])\n    elif len(p) == 3:\n        if str(p[2]).rstrip() == \",\":\n            p[0] = ExprList([p[1], p[2]])\n        else:\n            p[0] = ExprList([p[1]] + p[2])\n    else:\n        p[0] = ExprList([p[1]] + p[2] + [p[3]])\n\n\ndef p__comma_exprs(p):\n    \"\"\"_comma_exprs : COMMA expr\n                    | _comma_exprs COMMA expr\"\"\"\n    if len(p) == 3:\n        p[0] = [p[1], p[2]]\n    else:\n        p[0] = p[1] + [p[2], p[3]]\n\n\ndef p_testlist(p):\n    \"\"\"testlist : test\n                | test COMMA\n                | test _comma_tests\n                | test _comma_tests COMMA\"\"\"\n    if len(p) == 2:\n        p[0] = TestList([p[1]])\n    elif len(p) == 3:\n        if str(p[2]).rstrip() == \",\":\n            p[0] = TestList([p[1], p[2]])\n        else:\n            p[0] = TestList([p[1]] + p[2])\n    else:\n        p[0] = TestList([p[1]] + p[2] + [p[3]])\n\n\ndef p__comma_tests(p):\n    \"\"\"_comma_tests : COMMA test\n                    | _comma_tests COMMA test\"\"\"\n    if len(p) == 3:\n        p[0] = [p[1], p[2]]\n    else:\n        p[0] = p[1] + [p[2], p[3]]\n\n\ndef p_testlist_safe(p):\n    \"\"\"testlist_safe : test\n                     | test _comma_tests\"\"\"\n\n    if len(p) == 2:\n        p[0] = TestList([p[1]])\n    elif len(p) == 3:\n        p[0] = TestList([p[1]] + p[2])\n    else:\n        p[0] = TestList([p[1]] + p[2] + [p[3]])\n\n\ndef p_dictmaker(p):\n    \"\"\"dictmaker : _key_value_pair\n                 | _key_value_pair COMMA\n                 | _key_value_pair _comma_key_value_pairs\n                 | _key_value_pair _comma_key_value_pairs COMMA\"\"\"\n    if len(p) == 2:\n        p[0] = DictMaker(p[1])\n    elif len(p) == 3:\n        if str(p[2]).rstrip() == \",\":\n            p[0] = DictMaker(p[1] + [p[2]])\n        else:\n            p[0] = DictMaker(p[1] + p[2])\n    else:\n        p[0] = DictMaker(p[1] + p[2] + [p[3]])\n\n\ndef p__key_value_pair(p):\n    \"\"\"_key_value_pair : test LITTLEARROW test\"\"\"\n    p[0] = [p[1], p[2], p[3]]\n\n\ndef p__comma_key_value_pairs(p):\n    \"\"\"_comma_key_value_pairs : COMMA _key_value_pair\n                              | _comma_key_value_pairs COMMA _key_value_pair\"\"\"\n    if len(p) == 3:\n        p[0] = [p[1]] + p[2]\n    else:\n        p[0] = p[1] + [p[2]] + p[3]\n\n\ndef p_arglist(p):\n    \"\"\"arglist : _partial_arglist_starting_with_argument\n               | _partial_arglist_starting_with_keyword\n               | _partial_arglist_starting_with_star\n               | _partial_arglist_starting_with_doublestar\"\"\"\n    p[0] = ArgList(p[1])\n\n\ndef p__partial_arglist_starting_with_doublestar(p):\n    \"\"\"_partial_arglist_starting_with_doublestar : DOUBLESTAR test\"\"\"\n    p[0] = [p[1], p[2]]\n\n\ndef p__partial_arglist_starting_with_star(p):\n    \"\"\"_partial_arglist_starting_with_star : STAR test\n                                           | STAR test COMMA _partial_arglist_starting_with_doublestar\"\"\"\n    if len(p) == 3:\n        p[0] = [p[1], p[2]]\n    else:\n        p[0] = [p[1], p[2], p[3]] + p[4]\n\n\ndef p__partial_arglist_starting_with_keyword(p):\n    \"\"\"_partial_arglist_starting_with_keyword : _keyword_argument\n                                              | _keyword_argument COMMA _partial_arglist_starting_with_doublestar\n                                              | _keyword_argument COMMA _partial_arglist_starting_with_star\n                                              | _keyword_argument COMMA _partial_arglist_starting_with_keyword\"\"\"\n    if len(p) < 4:\n        p[0] = p[1:]\n    else:\n        p[0] = [p[1], p[2]] + p[3]\n\n\ndef p__partial_arglist_starting_with_argument(p):\n    \"\"\"_partial_arglist_starting_with_argument : COMMA\n                                               | COMMA _partial_arglist_starting_with_doublestar\n                                               | COMMA _partial_arglist_starting_with_star\n                                               | COMMA _partial_arglist_starting_with_keyword\n                                               | COMMA _partial_arglist_starting_with_argument\n                                               | argument\n                                               | argument COMMA\n                                               | argument COMMA _partial_arglist_starting_with_doublestar\n                                               | argument COMMA _partial_arglist_starting_with_star\n                                               | argument COMMA _partial_arglist_starting_with_keyword\n                                               | argument COMMA _partial_arglist_starting_with_argument\"\"\"\n    if len(p) < 4:\n        if not isinstance(p[1], ParseNode):\n            p[0] = [Argument([TestFromAtomChild(Name(['']))]), p[1]]\n            if len(p) > 2:\n                p[0] += p[2]\n        else:\n            p[0] = p[1:]\n    else:\n        p[0] = [p[1], p[2]] + p[3]\n\n\ndef p_argument(p):\n    \"\"\"argument : test\n                | test gen_for\"\"\"\n    p[0] = Argument(p[1:])\n\n\ndef p__keyword_argument(p):\n    \"\"\"_keyword_argument : test COLONEQUALS test\"\"\"\n    p[0] = Argument(p[1:])\n\n\ndef p_list_iter(p):\n    \"\"\"list_iter : list_for\n                 | list_if\"\"\"\n    p[0] = ListIter([p[1]])\n\n\ndef p_list_for(p):\n    \"\"\"list_for : FOR exprlist IN testlist_safe\n                | FOR exprlist IN testlist_safe list_iter\"\"\"\n    p[0] = ListFor(p[1:])\n\n\ndef p_list_if(p):\n    \"\"\"list_if : IF test\n               | IF test list_iter\"\"\"\n    p[0] = ListIf(p[1:])\n\n\ndef p_gen_iter(p):\n    \"\"\"gen_iter : gen_for\n                | gen_if\"\"\"\n    p[0] = GenIter([p[1]])\n\n\ndef p_gen_for(p):\n    \"\"\"gen_for : FOR exprlist IN test\n               | FOR exprlist IN test gen_iter\"\"\"\n    p[0] = GenFor(p[1:])\n\n\ndef p_gen_if(p):\n    \"\"\"gen_if : IF test\n              | IF test gen_iter\"\"\"\n    p[0] = GenIf(p[1:])\n\n\ndef p_testlist1(p):\n    \"\"\"testlist1 : test\n                 | test _comma_tests\"\"\"\n    if len(p) == 2:\n        p[0] = TestList([p[1]])\n    else:\n        p[0] = TestList([p[1]] + p[2])\n\n\ndef p__name(p):\n    \"\"\"_name : NAME\"\"\"\n    p[0] = Name([p[1]])\n\n\ndef p__number(p):\n    \"\"\"_number : _integer\n               | _float\n               | _imaginary\"\"\"\n    p[0] = Number([p[1]])\n\n\ndef p__imaginary(p):\n    \"\"\"_imaginary  : IMAGINARY\"\"\"\n    p[0] = p[1]\n\n\ndef p__float(p):\n    \"\"\"_float : FLOATNUMBER\"\"\"\n    p[0] = p[1]\n\n\ndef p__integer(p):\n    \"\"\"_integer : OCTINTEGER\n                | DECINTEGER\n                | HEXINTEGER\"\"\"\n    p[0] = p[1]\n\n\ndef p_stringliteral(p):\n    \"\"\"stringliteral : _shortstring\n                     | _longstring\n                     | stringliteral _shortstring\n                     | stringliteral _longstring\"\"\"\n    if len(p) == 2:\n        p[0] = StringLiteral([p[1]])\n    else:\n        p[0] = StringLiteral(p[1].children + [p[2]])\n\n\ndef p__shortstring(p):\n    \"\"\"_shortstring : SSHORTSTRING\n                    | DSHORTSTRING\"\"\"\n    p[0] = p[1]\n\n\ndef p__longstring(p):\n    \"\"\"_longstring : DLONGSTRING\n                   | SLONGSTRING\"\"\"\n    p[0] = p[1]\n\n\n# Extensions to Python:\n\ndef FixFunction(p):\n    p[0] = [Atom([Name([str(p[1])])]), Trailer(p[2:])]\n\ndef p_iserror_function(p):\n    \"\"\"iserror_function : ISERROR LEFTPAREN argument RIGHTPAREN\"\"\"\n    FixFunction(p)\n\n\ndef p_iserr_function(p):\n    \"\"\"iserr_function : ISERR LEFTPAREN argument RIGHTPAREN\"\"\"\n    FixFunction(p)\n\n\ndef p_if_function(p):\n    \"\"\"if_function : IF LEFTPAREN argument COMMA argument COMMA argument RIGHTPAREN\n                   | IF LEFTPAREN argument COMMA argument COMMA RIGHTPAREN\n                   | IF LEFTPAREN argument COMMA argument RIGHTPAREN\"\"\"\n    FixFunction(p)\n\n\ndef p_and_function(p):\n    \"\"\"and_function : AND LEFTPAREN arglist RIGHTPAREN\"\"\"\n    FixFunction(p)\n\n\ndef p_or_function(p):\n    \"\"\"or_function : OR LEFTPAREN arglist RIGHTPAREN\"\"\"\n    FixFunction(p)\n\n\ndef p_fl_reference(p):\n    \"\"\"fl_reference : atom\n                    | atom _trailers\n                    | and_function\n                    | or_function\n                    | if_function\n                    | if_function _trailers\n                    | iserr_function\n                    | iserror_function\n                    | fl_cell_range\n                    | fl_cell_range _trailers\n                    | fl_cell_reference\n                    | fl_cell_reference _trailers\n                    | fl_column_reference\n                    | fl_column_reference _trailers\n                    | fl_row_reference\n                    | fl_row_reference _trailers\n                    | fl_named_column_reference\n                    | fl_named_column_reference _trailers\n                    | fl_named_row_reference\n                    | fl_named_row_reference _trailers\n                    | fl_naked_worksheet_reference\n                    | fl_naked_worksheet_reference _trailers\n                    | fl_deleted_reference\n                    | fl_invalid_reference\n                    | fl_dde_call\"\"\"\n    if isinstance(p[1], ParseNode):\n        firstChildren = [p[1]]\n    else:\n        firstChildren = p[1]\n    if len(p) == 2:\n        p[0] = FLReference(firstChildren)\n    elif len(p) == 3:\n        p[0] = FLReference(firstChildren + p[2])\n\n\ndef p_fl_dde_call(p):\n    \"\"\"fl_dde_call : SSHORTSTRING EXCLAMATION SSHORTSTRING\"\"\"\n    p[0] = FLDDECall(p[1:])\n\n\ndef p_fl_cell_range(p):\n    \"\"\"fl_cell_range : fl_cell_reference COLON fl_cell_reference\n                     | fl_cell_reference COLON fl_deleted_reference\n                     | fl_deleted_reference COLON fl_cell_reference\n                     | fl_deleted_reference COLON fl_deleted_reference\n                     | fl_cell_reference COLON fl_invalid_reference\n                     | fl_invalid_reference COLON fl_cell_reference\n                     | fl_invalid_reference COLON fl_invalid_reference\"\"\"\n    types = [ParseNode.FL_CELL_REFERENCE, ParseNode.FL_DELETED_REFERENCE, ParseNode.FL_INVALID_REFERENCE]\n    if p[1].type not in types:\n        raise FormulaError(\"Error in formula at position %d: unexpected '%s'\" % (p.lexer.lexpos - len(p[1].flatten()) - len(p[3].flatten()) - len(p[2]) - 1, p[1].flatten()))\n    if p[3].type not in types:\n        raise FormulaError(\"Error in formula at position %d: unexpected '%s'\" % (p.lexer.lexpos - len(p[3].flatten()) - 1, p[3].flatten()))\n\n    if p[1].type == ParseNode.FL_CELL_REFERENCE and p[3].type == ParseNode.FL_CELL_REFERENCE:\n        if len(p[1].children) == 3 and len(p[3].children) == 1:\n            p[3].children[:0] = p[1].children[:2]\n\n    p[0] = FLCellRange([p[1], p[2], p[3]])\n\n\ndef GetWorksheetNameFromPotentialNakedWorksheetReference(nameOrNWRef):\n    worksheetName = nameOrNWRef\n    if isinstance(nameOrNWRef, ParseNode):\n        worksheetName = nameOrNWRef.children[1]\n        worksheetName += get_rstripped_part(nameOrNWRef.children[2])\n    return worksheetName\n\n\ndef _interpretHorribleFLReferenceMess(p, simpleInterpreter):\n    # this is used to deal with the half-dozen ways that valid cell, col, or row references may have been tokenised\n    p_1 = p[1]\n    if len(p) == 2:\n        if '!' not in p_1: #simple reference\n            p[0] = simpleInterpreter(p_1, []) or Atom([Name([p_1])])\n            return\n        else:\n            worksheetName, cellName = p_1.rsplit('!', 1)\n            exclamationMark = '!' + get_lstripped_part(cellName)\n            cellName = cellName.lstrip()\n    else:\n        worksheetName = GetWorksheetNameFromPotentialNakedWorksheetReference(p_1)\n        exclamationMark = p[2]\n        cellName = p[3]\n\n    result = simpleInterpreter(cellName, [worksheetName, exclamationMark])\n    if result is None:\n        raise FormulaError(\"Error in formula at position %d: unexpected '%s'\" % (p.lexer.lexpos - len(cellName) - 1, cellName))\n    else:\n        p[0] = result\n\n\ndef _asCellReference(cellName, worksheetInfo):\n    if cell_name_to_coordinates(cellName):\n        worksheetInfo.append(cellName)\n        return FLCellReference(worksheetInfo)\n\n\ndef p_fl_cell_reference(p):\n    \"\"\"fl_cell_reference : FLCELLREFLIKENAME\n                         | LONGCELLREFERENCE\n                         | FLCELLREFLIKENAME EXCLAMATION FLCELLREFLIKENAME\n                         | FLCOLUMNREFLIKENAME EXCLAMATION FLCELLREFLIKENAME\n                         | FLROWREFLIKENAME  EXCLAMATION FLCELLREFLIKENAME\n                         | NAME EXCLAMATION FLCELLREFLIKENAME\n                         | fl_naked_worksheet_reference EXCLAMATION FLCELLREFLIKENAME\"\"\"\n    _interpretHorribleFLReferenceMess(p, _asCellReference)\n\n\ndef _asColumnReference(columnName, worksheetInfo):\n    worksheetInfo.append(columnName)\n    columnReference = FLColumnReference(worksheetInfo)\n    if column_name_to_index(columnReference.plainColumnName):\n        return columnReference\n\n\ndef p_fl_column_reference(p):\n    \"\"\"fl_column_reference : FLCOLUMNREFLIKENAME\n                           | LONGCOLUMNREFERENCE\n                           | FLCELLREFLIKENAME EXCLAMATION FLCOLUMNREFLIKENAME\n                           | FLCOLUMNREFLIKENAME EXCLAMATION FLCOLUMNREFLIKENAME\n                           | FLROWREFLIKENAME EXCLAMATION FLCOLUMNREFLIKENAME\n                           | NAME EXCLAMATION FLCOLUMNREFLIKENAME\n                           | fl_naked_worksheet_reference EXCLAMATION FLCOLUMNREFLIKENAME\"\"\"\n    _interpretHorribleFLReferenceMess(p, _asColumnReference)\n\n\ndef _asRowReference(rowName, worksheetInfo):\n    worksheetInfo.append(rowName)\n    rowReference = FLRowReference(worksheetInfo)\n    rowIndex = int(rowReference.plainRowName)\n    if rowIndex < sys.maxint:\n        return rowReference\n\n\ndef p_fl_row_reference(p):\n    \"\"\"fl_row_reference : FLROWREFLIKENAME\n                        | LONGROWREFERENCE\n                        | FLCELLREFLIKENAME EXCLAMATION FLROWREFLIKENAME\n                        | FLCOLUMNREFLIKENAME EXCLAMATION FLROWREFLIKENAME\n                        | FLROWREFLIKENAME  EXCLAMATION FLROWREFLIKENAME\n                        | NAME EXCLAMATION FLROWREFLIKENAME\n                        | fl_naked_worksheet_reference EXCLAMATION FLROWREFLIKENAME\"\"\"\n    _interpretHorribleFLReferenceMess(p, _asRowReference)\n\n\ndef _asNamedColumnReference(columnName, worksheetInfo):\n    worksheetInfo.append(columnName)\n    return FLNamedColumnReference(worksheetInfo)\n\n\ndef p_fl_named_column_reference(p):\n    \"\"\"fl_named_column_reference : FLNAMEDCOLUMNREFERENCE\n                                 | LONGNAMEDCOLUMNREFERENCE\n                                 | FLCELLREFLIKENAME EXCLAMATION FLNAMEDCOLUMNREFERENCE\n                                 | FLCOLUMNREFLIKENAME EXCLAMATION FLNAMEDCOLUMNREFERENCE\n                                 | FLROWREFLIKENAME EXCLAMATION FLNAMEDCOLUMNREFERENCE\n                                 | NAME EXCLAMATION FLNAMEDCOLUMNREFERENCE\n                                 | fl_naked_worksheet_reference EXCLAMATION FLNAMEDCOLUMNREFERENCE\"\"\"\n    _interpretHorribleFLReferenceMess(p, _asNamedColumnReference)\n\n\ndef _asNamedRowReference(rowName, worksheetInfo):\n    worksheetInfo.append(rowName)\n    return FLNamedRowReference(worksheetInfo)\n\n\ndef p_fl_named_row_reference(p):\n    \"\"\"fl_named_row_reference : FLNAMEDROWREFERENCE\n                              | LONGNAMEDROWREFERENCE\n                              | FLCELLREFLIKENAME EXCLAMATION FLNAMEDROWREFERENCE\n                              | FLCOLUMNREFLIKENAME EXCLAMATION FLNAMEDROWREFERENCE\n                              | FLROWREFLIKENAME EXCLAMATION FLNAMEDROWREFERENCE\n                              | NAME EXCLAMATION FLNAMEDROWREFERENCE\n                              | fl_naked_worksheet_reference EXCLAMATION FLNAMEDROWREFERENCE\"\"\"\n    _interpretHorribleFLReferenceMess(p, _asNamedRowReference)\n\n\ndef p_fl_deleted_reference(p):\n    \"\"\"fl_deleted_reference : FLDELETEDREFERENCE\n                            | LONGDELETEDREFERENCE\n                            | FLCELLREFLIKENAME EXCLAMATION FLDELETEDREFERENCE\n                            | FLCOLUMNREFLIKENAME EXCLAMATION FLDELETEDREFERENCE\n                            | FLROWREFLIKENAME EXCLAMATION FLDELETEDREFERENCE\n                            | NAME EXCLAMATION FLDELETEDREFERENCE\n                            | fl_naked_worksheet_reference EXCLAMATION FLDELETEDREFERENCE\"\"\"\n    if len(p) == 2:\n        if p[1].count('!') == 1:\n            p[0] = FLDeletedReference([p[1]])\n        else:\n            worksheetReference, deletedReference = p[1].rsplit('#', 1)\n            worksheetName, exclamationWhitespace = worksheetReference.rsplit('!', 1)\n            p[0] = FLDeletedReference([worksheetName, '!' + exclamationWhitespace, '#' + deletedReference])\n    else:\n        worksheetName = GetWorksheetNameFromPotentialNakedWorksheetReference(p[1])\n        p[0] = FLDeletedReference([worksheetName, p[2], p[3]])\n\n\ndef p_fl_invalid_reference(p):\n    \"\"\"fl_invalid_reference : FLINVALIDREFERENCE\n                            | LONGINVALIDREFERENCE\n                            | FLCELLREFLIKENAME EXCLAMATION FLINVALIDREFERENCE\n                            | FLCOLUMNREFLIKENAME EXCLAMATION FLINVALIDREFERENCE\n                            | FLROWREFLIKENAME EXCLAMATION FLINVALIDREFERENCE\n                            | NAME EXCLAMATION FLINVALIDREFERENCE\n                            | fl_naked_worksheet_reference EXCLAMATION FLINVALIDREFERENCE\"\"\"\n    if len(p) == 2:\n        if p[1].count('!') == 1:\n            p[0] = FLInvalidReference([p[1]])\n        else:\n            worksheetReference, invalidReference = p[1].rsplit('#', 1)\n            worksheetName, exclamationWhitespace = worksheetReference.rsplit('!', 1)\n            p[0] = FLInvalidReference([worksheetName, '!' + exclamationWhitespace, '#' + invalidReference])\n    else:\n        worksheetName = GetWorksheetNameFromPotentialNakedWorksheetReference(p[1])\n        p[0] = FLInvalidReference([worksheetName, p[2], p[3]])\n\n\ndef p_fl_naked_worksheet_reference(p):\n    \"\"\"fl_naked_worksheet_reference : LESSTHAN NAME GREATERTHAN\n                                    | LESSTHAN FLCELLREFLIKENAME GREATERTHAN\n                                    | FLNAKEDWORKSHEETREFERENCE\"\"\"\n    if len(p) == 2:\n        name, greaterThanWhitespace = p[1].rsplit(\">\", 1)\n        greaterThan = \">\" + greaterThanWhitespace\n        lessThanWhitespace = get_lstripped_part(name[1:])\n        lessThan = \"<\" + lessThanWhitespace\n        name = name[1:].lstrip()\n        p[0] = FLNakedWorksheetReference([lessThan, name, greaterThan])\n    else:\n        p[0] = FLNakedWorksheetReference([p[1], p[2], p[3]])\n\n\ndef p_error(p):\n    if not p:\n        # EOF reached\n        raise FormulaError(\"Possibly incomplete formula\")\n\n    raise FormulaError(\"Error in formula at position %d: unexpected '%s'\" % (p.lexpos + 1, p.value))\n"
  },
  {
    "path": "dirigible/sheet/parser/parse_node.py",
    "content": "# Copyright (c) 2005-2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\n\nclass ParseNode(object):\n\n    AND_TEST                        = \"AND_TEST\"\n    ARG_LIST                        = \"ARG_LIST\"\n    ARGUMENT                        = \"ARGUMENT\"\n    ARITH_EXPR                      = \"ARITH_EXPR\"\n    ATOM                            = \"ATOM\"\n    COMP_OPERATOR                   = \"COMP_OPERATOR\"\n    COMPARISON                      = \"COMPARISON\"\n    CONCAT_EXPR                     = \"CONCAT_EXPR\"\n    DICT_MAKER                      = \"DICT_MAKER\"\n    EXPR                            = \"EXPR\"\n    EXPR_LIST                       = \"EXPR_LIST\"\n    EXTENDED_SLICING                = \"EXTENDED_SLICING\"\n    FACTOR                          = \"FACTOR\"\n    FL_CELL_RANGE                   = \"FL_CELL_RANGE\"\n    FL_CELL_REFERENCE               = \"FL_CELL_REFERENCE\"\n    FL_COLUMN_REFERENCE             = \"FL_COLUMN_REFERENCE\"\n    FL_ROW_REFERENCE                = \"FL_ROW_REFERENCE\"\n    FL_DDE_CALL                     = \"FL_DDE_CALL\"\n    FL_DELETED_REFERENCE            = \"FL_DELETED_REFERENCE\"\n    FL_INVALID_REFERENCE            = \"FL_INVALID_REFERENCE\"\n    FL_NAKED_WORKSHEET_REFERENCE    = \"FL_NAKED_WORKSHEET_REFERENCE\"\n    FL_NAMED_COLUMN_REFERENCE       = \"FL_NAMED_COLUMN_REFERENCE\"\n    FL_NAMED_ROW_REFERENCE          = \"FL_NAMED_ROW_REFERENCE\"\n    FL_REFERENCE                    = \"FL_REFERENCE\"\n    FL_ROOT                         = \"FL_ROOT\"\n    FP_DEF                          = \"FP_DEF\"\n    FP_LIST                         = \"FP_LIST\"\n    GEN_FOR                         = \"GEN_FOR\"\n    GEN_IF                          = \"GEN_IF\"\n    GEN_ITER                        = \"GEN_ITER\"\n    LAMBDEF                         = \"LAMBDEF\"\n    LIST_FOR                        = \"LIST_FOR\"\n    LIST_IF                         = \"LIST_IF\"\n    LIST_ITER                       = \"LIST_ITER\"\n    LIST_MAKER                      = \"LIST_MAKER\"\n    NAME                            = \"NAME\"\n    NOT_TEST                        = \"NOT_TEST\"\n    NUMBER                          = \"NUMBER\"\n    PERCENT                         = \"PERCENT\"\n    POWER                           = \"POWER\"\n    SHIFT_EXPR                      = \"SHIFT_EXPR\"\n    SLICE_OP                        = \"SLICE_OP\"\n    STRINGLITERAL                   = \"STRINGLITERAL\"\n    SUBSCRIPT                       = \"SUBSCRIPT\"\n    SUBSCRIPT_LIST                  = \"SUBSCRIPT_LIST\"\n    SUM_ARGLIST                     = \"SUM_ARGLIST\"\n    TERM                            = \"TERM\"\n    TEST                            = \"TEST\"\n    TEST_LIST                       = \"TEST_LIST\"\n    TEST_LIST_GEXP                  = \"TEST_LIST_GEXP\"\n    TRAILER                         = \"TRAILER\"\n    VAR_ARGS_LIST                   = \"VAR_ARGS_LIST\"\n\n\n    def __init__(self, _type, children):\n        self.type = _type\n        self.children = children\n\n\n    def __repr__(self):\n        if self.children:\n            childrenStr = \" children=%r\" % (self.children)\n        else:\n            childrenStr = \"\"\n        return \"<%s type=\\\"%s\\\"%s>\" % (self.__class__.__name__, self.type, childrenStr)\n\n    __str__ = __repr__\n\n    def __eq__(self, other):\n        if other is None:\n            return False\n        if type(self) != type(other):\n            return False\n        if self.type != other.type:\n            return False\n        if hasattr(other.children, \"__len__\") ^ hasattr(self.children, \"__len__\"):\n            return False\n        if len(self.children) != len(other.children):\n            return False\n        for selfChild, otherChild in zip(self.children, other.children):\n            if selfChild != otherChild:\n                return False\n\n        return True\n\n\n    def __ne__(self, other):\n        return not self.__eq__(other)\n\n\n    def __hash__(self):\n        raise TypeError(\"mutable objects are unhashable\")\n\n\n    def flatten(self):\n        def AppendChild(string, child):\n            if isinstance(child, ParseNode):\n                return string + child.flatten()\n            return string + child if child is not None else string\n        return reduce(AppendChild, self.children, \"\")\n\n\n    classRegistry = {}\n\n    @classmethod\n    def register_node_type(cls, nodeType, nodeClass):\n        cls.classRegistry[nodeType] = nodeClass\n\n\n    @classmethod\n    def construct_node(cls, nodeType, children):\n        if nodeType in cls.classRegistry:\n            retVal = cls.classRegistry[nodeType](children)\n            return retVal\n        else:\n            return cls(nodeType, children)\n"
  },
  {
    "path": "dirigible/sheet/parser/parse_node_constructors.py",
    "content": "# Copyright (c) 2005-2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\nfrom sheet.parser.fl_cell_reference_parse_node import FLCellReferenceParseNode as FLCellReference\nfrom sheet.parser.fl_cell_range_parse_node import FLCellRangeParseNode as FLCellRange\nfrom sheet.parser.fl_column_reference_parse_node import FLColumnReferenceParseNode as FLColumnReference\nfrom sheet.parser.fl_named_column_reference_parse_node import FLNamedColumnReferenceParseNode as FLNamedColumnReference\nfrom sheet.parser.fl_named_row_reference_parse_node import FLNamedRowReferenceParseNode as FLNamedRowReference\nfrom sheet.parser.fl_row_reference_parse_node import FLRowReferenceParseNode as FLRowReference\nfrom sheet.parser.parse_node import ParseNode\n\n\ndef ConcatExpr(children):\n    return ParseNode.construct_node(ParseNode.CONCAT_EXPR, children)\n\ndef AndTest(children):\n    return ParseNode.construct_node(ParseNode.AND_TEST, children)\n\ndef ArgList(children):\n    return ParseNode.construct_node(ParseNode.ARG_LIST, children)\n\ndef Argument(children):\n    return ParseNode.construct_node(ParseNode.ARGUMENT, children)\n\ndef ArithExpr(children):\n    return ParseNode.construct_node(ParseNode.ARITH_EXPR, children)\n\ndef Atom(children):\n    return ParseNode.construct_node(ParseNode.ATOM, children)\n\ndef FLDDECall(children):\n    return ParseNode.construct_node(ParseNode.FL_DDE_CALL, children)\n\ndef FLDeletedReference(children):\n    return ParseNode.construct_node(ParseNode.FL_DELETED_REFERENCE, children)\n\ndef FLInvalidReference(children):\n    return ParseNode.construct_node(ParseNode.FL_INVALID_REFERENCE, children)\n\ndef FLNakedWorksheetReference(children):\n    return ParseNode.construct_node(ParseNode.FL_NAKED_WORKSHEET_REFERENCE, children)\n\ndef FLReference(children):\n    return ParseNode.construct_node(ParseNode.FL_REFERENCE, children)\n\ndef FLRoot(children):\n    return ParseNode.construct_node(ParseNode.FL_ROOT, children)\n\ndef Comparison(children):\n    return ParseNode.construct_node(ParseNode.COMPARISON, children)\n\ndef CompOperator(children):\n    return ParseNode.construct_node(ParseNode.COMP_OPERATOR, children)\n\ndef DictMaker(children):\n    return ParseNode.construct_node(ParseNode.DICT_MAKER, children)\n\ndef Expr(children):\n    return ParseNode.construct_node(ParseNode.EXPR, children)\n\ndef ExprList(children):\n    return ParseNode.construct_node(ParseNode.EXPR_LIST, children)\n\ndef Factor(children):\n    return ParseNode.construct_node(ParseNode.FACTOR, children)\n\ndef FPDef(children):\n    return ParseNode.construct_node(ParseNode.FP_DEF, children)\n\ndef FPList(children):\n    return ParseNode.construct_node(ParseNode.FP_LIST, children)\n\ndef GenFor(children):\n    return ParseNode.construct_node(ParseNode.GEN_FOR, children)\n\ndef GenIf(children):\n    return ParseNode.construct_node(ParseNode.GEN_IF, children)\n\ndef GenIter(children):\n    return ParseNode.construct_node(ParseNode.GEN_ITER, children)\n\ndef LambDef(children):\n    return ParseNode.construct_node(ParseNode.LAMBDEF, children)\n\ndef ListFor(children):\n    return ParseNode.construct_node(ParseNode.LIST_FOR, children)\n\ndef ListIf(children):\n    return ParseNode.construct_node(ParseNode.LIST_IF, children)\n\ndef ListIter(children):\n    return ParseNode.construct_node(ParseNode.LIST_ITER, children)\n\ndef ListMaker(children):\n    return ParseNode.construct_node(ParseNode.LIST_MAKER, children)\n\ndef Name(children):\n    return ParseNode.construct_node(ParseNode.NAME, children)\n\ndef NotTest(children):\n    return ParseNode.construct_node(ParseNode.NOT_TEST, children)\n\ndef Number(children):\n    return ParseNode.construct_node(ParseNode.NUMBER, children)\n\ndef Percent(children):\n    return ParseNode.construct_node(ParseNode.PERCENT, children)\n\ndef Power(children):\n    return ParseNode.construct_node(ParseNode.POWER, children)\n\ndef ShiftExpr(children):\n    return ParseNode.construct_node(ParseNode.SHIFT_EXPR, children)\n\ndef SliceOp(children):\n    return ParseNode.construct_node(ParseNode.SLICE_OP, children)\n\ndef StringLiteral(children):\n    return ParseNode.construct_node(ParseNode.STRINGLITERAL, children)\n\ndef Subscript(children):\n    return ParseNode.construct_node(ParseNode.SUBSCRIPT, children)\n\ndef SubscriptList(children):\n    return ParseNode.construct_node(ParseNode.SUBSCRIPT_LIST, children)\n\ndef Term(children):\n    return ParseNode.construct_node(ParseNode.TERM, children)\n\ndef Test(children):\n    return ParseNode.construct_node(ParseNode.TEST, children)\n\ndef TestList(children):\n    return ParseNode.construct_node(ParseNode.TEST_LIST, children)\n\ndef TestListGexp(children):\n    return ParseNode.construct_node(ParseNode.TEST_LIST_GEXP, children)\n\ndef Trailer(children):\n    return ParseNode.construct_node(ParseNode.TRAILER, children)\n\ndef VarArgsList(children):\n    return ParseNode.construct_node(ParseNode.VAR_ARGS_LIST, children)\n\n# combinations\n\ndef ArithExpr_Term(mExprChild):\n    return ArithExpr([Term([mExprChild])])\n\ndef Expr_ConcatExpr_ShiftExpr(shiftExprChild):\n    return Expr([ConcatExpr([ShiftExpr([shiftExprChild])])])\n\ndef Factor_Power_FLReference_Atom(*atomChildren):\n    return Factor([Power([FLReference([Atom(list(atomChildren))])])])\n\ndef Test_AndTest_NotTest_Comparison(comparisonChild):\n    return Test([AndTest([NotTest([Comparison([comparisonChild])])])])\n\n# very big combinations\n\ndef ExprFromAtomChild(atomChild):\n    return Expr_ConcatExpr_ShiftExpr(\n        ArithExpr_Term(Factor_Power_FLReference_Atom(atomChild)))\n\ndef ExprFromAtomChildren(atomChildren):\n    return Expr_ConcatExpr_ShiftExpr(\n        ArithExpr_Term(Factor_Power_FLReference_Atom(*atomChildren)))\n\ndef ExprFromNameChild(nameChild):\n    return ExprFromAtomChild(Name([nameChild]))\n\ndef TestFromAtomChild(atomChild):\n    return Test_AndTest_NotTest_Comparison(\n        ExprFromAtomChild(atomChild))\n\ndef TestFromPowerChild(powerChild):\n    return Test_AndTest_NotTest_Comparison(\n        Expr_ConcatExpr_ShiftExpr(\n            ArithExpr_Term(\n                Factor([Power([powerChild])]))))\n"
  },
  {
    "path": "dirigible/sheet/parser/parser.py",
    "content": "# Copyright (c) 2005-2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\nimport os\nfrom ply import lex\nfrom ply import yacc\nfrom threading import Lock\n\nfrom . import grammar, tokens\n\n\n_parser = yacc.yacc(\n    module=grammar,\n    outputdir=os.path.dirname(__file__),\n    method=\"LALR\",\n    debug=0,\n    tabmodule='sheet.parser.parsetab'\n)\n_lexer = lex.lex(tokens)\n\n_parser_lock = Lock()\n\n\ndef parse(string):\n    _parser_lock.acquire()\n    try:\n        return _parser.parse(string, _lexer)\n    finally:\n        _parser_lock.release()\n\n"
  },
  {
    "path": "dirigible/sheet/parser/parsetab.py",
    "content": "\n# /home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/parsetab.py\n# This file is automatically generated. Do not edit.\n_tabversion = '3.2'\n\n_lr_method = 'LALR'\n\n_lr_signature = 'NI\\x83r\\x17_\\xb5\\xd2\\x19\\xd5\\xfc_\\xfd.\\xc1%'\n    \n_lr_action_items = {'EXCLAMATION':([10,33,40,41,52,72,76,224,226,227,228,229,255,256,257,258,259,269,270,272,273,274,291,292,],[83,113,130,131,135,170,-304,350,351,352,353,354,371,372,373,374,375,376,377,378,379,380,-303,-302,]),'STAR':([3,5,6,8,9,10,11,13,14,16,20,21,22,23,24,27,30,31,33,34,35,36,37,38,39,40,41,42,43,45,46,47,48,49,50,51,52,54,55,56,58,62,63,64,65,66,67,69,72,76,77,78,79,80,81,82,84,85,86,92,93,94,95,99,102,103,105,114,115,118,129,133,134,149,158,160,163,165,175,178,179,180,181,182,183,184,189,193,197,207,208,216,222,223,225,226,232,238,239,240,241,242,243,244,247,253,254,256,260,261,262,263,264,265,266,267,268,271,272,275,276,277,278,279,280,281,290,291,292,295,300,301,302,303,308,309,310,311,312,313,314,319,320,322,323,324,335,343,349,384,385,386,387,388,392,413,430,441,445,],[-296,-261,-205,-84,-268,-260,-234,-200,-204,-94,-289,-93,-206,-86,-254,-230,-211,-95,-240,-224,126,-243,-228,-274,-281,-209,-267,-242,-227,-212,-199,-210,-223,-202,-222,-220,-196,-197,-295,-238,-201,-226,-198,157,-236,-282,-275,-288,-253,-304,-244,-203,-232,-90,-87,-88,186,-235,-114,-208,-207,-209,186,-99,186,-231,-101,-241,-225,-196,-229,-89,-221,-239,296,-85,-237,-97,-233,-277,-263,-270,-256,-284,-298,-291,-111,186,-115,-110,-109,-100,-246,-250,-247,-253,-102,-280,-266,-273,-259,-287,-301,-294,126,-251,-252,-253,-245,-278,-264,-271,-257,-285,-299,-292,-248,-249,-253,-279,-265,-258,-286,-300,-293,-272,-96,-303,-302,-76,-79,-78,-77,-98,-276,-262,-269,-255,-283,-297,-290,-91,-92,-112,186,186,-113,-219,-218,-213,-80,-83,-82,-81,-214,126,-217,-216,-215,]),'SLASH':([3,5,6,8,9,10,11,13,14,16,20,21,22,23,24,27,30,31,33,34,36,37,38,39,40,41,42,43,45,46,47,48,49,50,51,52,54,55,56,58,62,63,64,65,66,67,69,72,76,77,78,79,80,81,82,85,86,92,93,94,99,103,105,114,115,118,129,133,134,149,158,160,163,165,175,178,179,180,181,182,183,184,189,197,207,208,216,222,223,225,226,232,238,239,240,241,242,243,244,253,254,256,260,261,262,263,264,265,266,267,268,271,272,275,276,277,278,279,280,281,290,291,292,295,300,301,302,303,308,309,310,311,312,313,314,319,320,322,335,343,349,384,385,386,387,388,392,430,441,445,],[-296,-261,-205,-84,-268,-260,-234,-200,-204,-94,-289,-93,-206,-86,-254,-230,-211,-95,-240,-224,-243,-228,-274,-281,-209,-267,-242,-227,-212,-199,-210,-223,-202,-222,-220,-196,-197,-295,-238,-201,-226,-198,162,-236,-282,-275,-288,-253,-304,-244,-203,-232,-90,-87,-88,-235,-114,-208,-207,-209,-99,-231,-101,-241,-225,-196,-229,-89,-221,-239,299,-85,-237,-97,-233,-277,-263,-270,-256,-284,-298,-291,-111,-115,-110,-109,-100,-246,-250,-247,-253,-102,-280,-266,-273,-259,-287,-301,-294,-251,-252,-253,-245,-278,-264,-271,-257,-285,-299,-292,-248,-249,-253,-279,-265,-258,-286,-300,-293,-272,-96,-303,-302,-76,-79,-78,-77,-98,-276,-262,-269,-255,-283,-297,-290,-91,-92,-112,-113,-219,-218,-213,-80,-83,-82,-81,-214,-217,-216,-215,]),'FLOATNUMBER':([2,4,7,19,28,32,44,59,70,84,87,90,91,95,96,102,109,111,117,136,137,138,139,140,141,142,143,144,145,146,150,156,157,159,161,162,169,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,283,284,285,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,-46,-48,58,-49,-50,-45,-44,-47,-42,-43,-51,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,-52,58,-53,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,]),'OBSOLETEUNEQUAL':([3,5,6,8,9,10,11,13,14,15,16,18,20,21,22,23,24,27,29,30,31,33,34,36,37,38,39,40,41,42,43,45,46,47,48,49,50,51,52,53,54,55,56,58,62,63,64,65,66,67,69,72,73,76,77,78,79,80,81,82,85,86,89,92,93,94,97,99,103,105,110,114,115,118,129,133,134,147,149,158,160,163,165,172,175,178,179,180,181,182,183,184,189,197,207,208,211,212,214,216,222,223,225,226,232,234,237,238,239,240,241,242,243,244,253,254,256,260,261,262,263,264,265,266,267,268,271,272,275,276,277,278,279,280,281,282,290,291,292,295,300,301,302,303,308,309,310,311,312,313,314,315,319,320,322,335,341,342,343,344,349,358,359,381,384,385,386,387,388,391,392,430,441,445,],[-296,-261,-205,-84,-268,-260,-234,-200,-204,-68,-94,-58,-289,-93,-206,-86,-254,-230,-62,-211,-95,-240,-224,-243,-228,-274,-281,-209,-267,-242,-227,-212,-199,-210,-223,-202,-222,-220,-196,139,-197,-295,-238,-201,-226,-198,-74,-236,-282,-275,-288,-253,-54,-304,-244,-203,-232,-90,-87,-88,-235,-114,-69,-208,-207,-209,-59,-99,-231,-101,-63,-241,-225,-196,-229,-89,-221,139,-239,-75,-85,-237,-97,-55,-233,-277,-263,-270,-256,-284,-298,-291,-111,-115,-110,-109,-70,-71,-60,-100,-246,-250,-247,-253,-102,-64,-65,-280,-266,-273,-259,-287,-301,-294,-251,-252,-253,-245,-278,-264,-271,-257,-285,-299,-292,-248,-249,-253,-279,-265,-258,-286,-300,-293,-272,-40,-96,-303,-302,-76,-79,-78,-77,-98,-276,-262,-269,-255,-283,-297,-290,-56,-91,-92,-112,-113,-72,-73,-219,-61,-218,-66,-67,-41,-213,-80,-83,-82,-81,-57,-214,-217,-216,-215,]),'BACKWARDQUOTE':([2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,18,19,20,21,22,23,24,26,27,28,29,30,31,32,33,34,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,62,63,64,65,66,67,69,70,71,72,73,76,77,78,79,80,81,82,84,85,86,87,89,90,91,92,93,94,95,96,97,99,102,103,105,109,110,111,112,114,115,117,118,129,133,134,136,137,138,139,140,141,142,143,144,145,146,147,149,150,151,152,153,156,157,158,159,160,161,162,163,165,168,169,171,172,173,174,175,176,177,178,179,180,181,182,183,184,186,189,192,193,197,206,207,208,209,210,211,212,214,215,216,218,220,222,223,225,226,230,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,250,253,254,256,260,261,262,263,264,265,266,267,268,271,272,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,295,296,297,298,299,300,301,302,303,304,306,307,308,309,310,311,312,313,314,315,316,319,320,322,323,324,332,334,335,336,338,341,342,343,344,345,348,349,356,358,359,369,381,382,383,384,385,386,387,388,390,391,392,393,406,408,409,410,417,424,429,430,434,439,441,445,],[59,-296,59,-261,-205,59,-84,-268,-260,-234,-29,-200,-204,-68,-94,-58,59,-289,-93,-206,-86,-254,-36,-230,59,-62,-211,-95,59,-240,-224,-243,-228,-274,-281,-209,-267,-242,-227,59,-212,-199,-210,-223,-202,-222,-220,-196,-38,-197,-295,-238,-32,-201,59,-226,-198,-74,-236,-282,-275,-288,59,-27,-253,-54,-304,-244,-203,-232,-90,-87,-88,59,-235,-114,59,-69,59,59,-208,-207,-209,59,59,-59,-99,59,-231,-101,59,-63,59,-37,-241,-225,59,-196,-229,-89,-221,-46,-48,59,-49,-50,-45,-44,-47,-42,-43,-51,-39,-239,59,-33,-194,290,59,59,-75,59,-85,59,59,-237,-97,-28,59,59,-55,59,59,-233,59,59,-277,-263,-270,-256,-284,-298,-291,59,-111,59,59,-115,59,-110,-109,59,59,-70,-71,-60,59,-100,59,59,-246,-250,-247,-253,59,-102,59,-64,59,59,-65,-280,-266,-273,-259,-287,-301,-294,-107,59,59,-251,-252,-253,-245,-278,-264,-271,-257,-285,-299,-292,-248,-249,-253,-279,-265,-258,-286,-300,-293,-272,-40,-52,59,-53,-34,59,-195,59,-96,-303,-302,-76,59,59,59,59,-79,-78,-77,-98,59,59,-30,-276,-262,-269,-255,-283,-297,-290,-56,59,-91,-92,-112,59,59,59,59,-113,59,59,-72,-73,-219,-61,59,-146,-218,59,-66,-67,-108,-41,-35,59,-213,-80,-83,-82,-81,-31,-57,-214,59,59,-147,59,59,59,59,59,-217,59,59,-216,-215,]),'RIGHTBRACKET':([3,5,6,8,9,10,11,12,13,14,15,16,18,19,20,21,22,23,24,26,27,29,30,31,33,34,36,37,38,39,40,41,42,43,45,46,47,48,49,50,51,52,53,54,55,56,57,58,62,63,64,65,66,67,69,71,72,73,76,77,78,79,80,81,82,85,86,89,92,93,94,97,98,99,100,101,103,105,110,112,114,115,118,129,133,134,147,149,151,158,160,163,165,168,172,175,178,179,180,181,182,183,184,189,197,198,199,201,202,203,204,205,206,207,208,211,212,214,216,217,219,220,222,223,225,226,232,234,237,238,239,240,241,242,243,244,245,253,254,256,260,261,262,263,264,265,266,267,268,271,272,275,276,277,278,279,280,281,282,286,290,291,292,295,300,301,302,303,307,308,309,310,311,312,313,314,315,319,320,322,333,334,335,336,337,338,339,340,341,342,343,344,345,348,349,358,359,369,381,382,384,385,386,387,388,390,391,392,404,405,406,407,408,420,421,422,430,431,432,433,435,441,443,445,446,],[-296,-261,-205,-84,-268,-260,-234,-29,-200,-204,-68,-94,-58,99,-289,-93,-206,-86,-254,-36,-230,-62,-211,-95,-240,-224,-243,-228,-274,-281,-209,-267,-242,-227,-212,-199,-210,-223,-202,-222,-220,-196,-38,-197,-295,-238,-32,-201,-226,-198,-74,-236,-282,-275,-288,-27,-253,-54,-304,-244,-203,-232,-90,-87,-88,-235,-114,-69,-208,-207,-209,-59,-103,-99,216,-142,-231,-101,-63,-37,-241,-225,-196,-229,-89,-221,-39,-239,-33,-75,-85,-237,-97,-28,-55,-233,-277,-263,-270,-256,-284,-298,-291,-111,-115,-126,335,-122,-116,-123,-124,-125,-128,-110,-109,-70,-71,-60,-100,-144,-104,-143,-246,-250,-247,-253,-102,-64,-65,-280,-266,-273,-259,-287,-301,-294,-107,-251,-252,-253,-245,-278,-264,-271,-257,-285,-299,-292,-248,-249,-253,-279,-265,-258,-286,-300,-293,-272,-40,-34,-96,-303,-302,-76,-79,-78,-77,-98,-30,-276,-262,-269,-255,-283,-297,-290,-56,-91,-92,-112,-127,-134,-113,-129,-118,-117,-130,-133,-72,-73,-219,-61,-145,-146,-218,-66,-67,-108,-41,-35,-213,-80,-83,-82,-81,-31,-57,-214,-135,-131,-119,-120,-147,-121,-184,-148,-217,-185,-183,-182,-149,-216,-186,-215,-187,]),'FLNAMEDCOLUMNREFERENCE':([2,4,7,19,28,32,44,59,70,83,84,87,90,91,95,96,102,109,111,113,117,131,135,136,137,138,139,140,141,142,143,144,145,146,150,156,157,159,161,162,169,170,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,283,284,285,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[38,38,38,38,38,38,38,38,38,178,38,38,38,38,38,38,38,38,38,238,38,261,275,-46,-48,38,-49,-50,-45,-44,-47,-42,-43,-51,38,38,38,38,38,38,38,308,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,-52,38,-53,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,38,]),'LESSEQUAL':([3,5,6,8,9,10,11,13,14,15,16,18,20,21,22,23,24,27,29,30,31,33,34,36,37,38,39,40,41,42,43,45,46,47,48,49,50,51,52,53,54,55,56,58,62,63,64,65,66,67,69,72,73,76,77,78,79,80,81,82,85,86,89,92,93,94,97,99,103,105,110,114,115,118,129,133,134,147,149,158,160,163,165,172,175,178,179,180,181,182,183,184,189,197,207,208,211,212,214,216,222,223,225,226,232,234,237,238,239,240,241,242,243,244,253,254,256,260,261,262,263,264,265,266,267,268,271,272,275,276,277,278,279,280,281,282,290,291,292,295,300,301,302,303,308,309,310,311,312,313,314,315,319,320,322,335,341,342,343,344,349,358,359,381,384,385,386,387,388,391,392,430,441,445,],[-296,-261,-205,-84,-268,-260,-234,-200,-204,-68,-94,-58,-289,-93,-206,-86,-254,-230,-62,-211,-95,-240,-224,-243,-228,-274,-281,-209,-267,-242,-227,-212,-199,-210,-223,-202,-222,-220,-196,143,-197,-295,-238,-201,-226,-198,-74,-236,-282,-275,-288,-253,-54,-304,-244,-203,-232,-90,-87,-88,-235,-114,-69,-208,-207,-209,-59,-99,-231,-101,-63,-241,-225,-196,-229,-89,-221,143,-239,-75,-85,-237,-97,-55,-233,-277,-263,-270,-256,-284,-298,-291,-111,-115,-110,-109,-70,-71,-60,-100,-246,-250,-247,-253,-102,-64,-65,-280,-266,-273,-259,-287,-301,-294,-251,-252,-253,-245,-278,-264,-271,-257,-285,-299,-292,-248,-249,-253,-279,-265,-258,-286,-300,-293,-272,-40,-96,-303,-302,-76,-79,-78,-77,-98,-276,-262,-269,-255,-283,-297,-290,-56,-91,-92,-112,-113,-72,-73,-219,-61,-218,-66,-67,-41,-213,-80,-83,-82,-81,-57,-214,-217,-216,-215,]),'LESSTHAN':([2,3,4,5,6,7,8,9,10,11,13,14,15,16,18,19,20,21,22,23,24,27,28,29,30,31,32,33,34,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,58,59,62,63,64,65,66,67,69,70,72,73,76,77,78,79,80,81,82,84,85,86,87,89,90,91,92,93,94,95,96,97,99,102,103,104,105,109,110,111,114,115,117,118,128,129,132,133,134,136,137,138,139,140,141,142,143,144,145,146,147,149,150,156,157,158,159,160,161,162,163,165,169,171,172,173,174,175,176,177,178,179,180,181,182,183,184,186,189,192,193,197,206,207,208,209,210,211,212,214,215,216,218,220,222,223,225,226,230,232,233,234,235,236,237,238,239,240,241,242,243,244,246,250,253,254,256,260,261,262,263,264,265,266,267,268,271,272,275,276,277,278,279,280,281,282,283,284,285,287,289,290,291,292,295,296,297,298,299,300,301,302,303,304,306,308,309,310,311,312,313,314,315,316,319,320,322,323,324,332,334,335,336,338,341,342,343,344,345,349,356,358,359,381,383,384,385,386,387,388,391,392,393,406,409,410,417,424,429,430,434,439,441,445,],[60,-296,60,-261,-205,60,-84,-268,-260,-234,-200,-204,-68,-94,-58,60,-289,-93,-206,-86,-254,-230,60,-62,-211,-95,60,-240,-224,-243,-228,-274,-281,-209,-267,-242,-227,60,-212,-199,-210,-223,-202,-222,-220,-196,144,-197,-295,-238,-201,60,-226,-198,-74,-236,-282,-275,-288,60,-253,-54,-304,-244,-203,-232,-90,-87,-88,60,-235,-114,60,-69,60,60,-208,-207,-209,60,60,-59,-99,60,-231,60,-101,60,-63,60,-241,-225,60,-196,60,-229,60,-89,-221,-46,-48,60,-49,-50,-45,-44,-47,-42,-43,-51,144,-239,60,60,60,-75,60,-85,60,60,-237,-97,60,60,-55,60,60,-233,60,60,-277,-263,-270,-256,-284,-298,-291,60,-111,60,60,-115,60,-110,-109,60,60,-70,-71,-60,60,-100,60,60,-246,-250,-247,-253,60,-102,60,-64,60,60,-65,-280,-266,-273,-259,-287,-301,-294,60,60,-251,-252,-253,-245,-278,-264,-271,-257,-285,-299,-292,-248,-249,-253,-279,-265,-258,-286,-300,-293,-272,-40,-52,60,-53,60,60,-96,-303,-302,-76,60,60,60,60,-79,-78,-77,-98,60,60,-276,-262,-269,-255,-283,-297,-290,-56,60,-91,-92,-112,60,60,60,60,-113,60,60,-72,-73,-219,-61,60,-218,60,-66,-67,-41,60,-213,-80,-83,-82,-81,-57,-214,60,60,60,60,60,60,60,-217,60,60,-216,-215,]),'FLCELLREFLIKENAME':([2,4,7,19,28,32,44,59,60,70,83,84,87,88,90,91,95,96,102,104,109,111,113,117,128,131,132,135,136,137,138,139,140,141,142,143,144,145,146,150,156,157,159,161,162,169,170,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,283,284,285,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,350,351,352,353,354,356,371,372,373,374,375,376,377,378,379,380,383,393,406,409,410,417,424,429,434,439,],[72,72,72,72,72,72,72,72,154,72,181,72,72,207,72,72,72,72,72,226,72,72,241,72,256,264,272,277,-46,-48,72,-49,-50,-45,-44,-47,-42,-43,-51,72,72,72,72,72,72,72,311,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,-52,72,-53,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,72,264,311,241,181,277,72,264,311,241,181,277,181,264,311,241,277,72,72,72,72,72,72,72,72,72,72,]),'FLNAMEDROWREFERENCE':([2,4,7,19,28,32,44,59,70,83,84,87,90,91,95,96,102,109,111,113,117,131,135,136,137,138,139,140,141,142,143,144,145,146,150,156,157,159,161,162,169,170,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,283,284,285,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[39,39,39,39,39,39,39,39,39,182,39,39,39,39,39,39,39,39,39,242,39,265,278,-46,-48,39,-49,-50,-45,-44,-47,-42,-43,-51,39,39,39,39,39,39,39,312,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,-52,39,-53,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,39,]),'FOR':([3,5,6,8,9,10,11,12,13,14,15,16,18,20,21,22,23,24,26,27,29,30,31,33,34,36,37,38,39,40,41,42,43,45,46,47,48,49,50,51,52,53,54,55,56,57,58,62,63,64,65,66,67,69,71,72,73,76,77,78,79,80,81,82,85,86,89,92,93,94,97,99,101,103,105,110,112,114,115,118,129,133,134,147,149,151,158,160,163,165,167,168,172,175,178,179,180,181,182,183,184,189,194,197,207,208,211,212,214,216,222,223,225,226,232,234,237,238,239,240,241,242,243,244,245,253,254,256,260,261,262,263,264,265,266,267,268,271,272,275,276,277,278,279,280,281,282,286,290,291,292,294,295,300,301,302,303,307,308,309,310,311,312,313,314,315,319,320,322,328,335,341,342,343,344,348,349,358,359,369,381,382,384,385,386,387,388,390,391,392,408,421,422,428,430,435,441,443,444,445,],[-296,-261,-205,-84,-268,-260,-234,-29,-200,-204,-68,-94,-58,-289,-93,-206,-86,-254,-36,-230,-62,-211,-95,-240,-224,-243,-228,-274,-281,-209,-267,-242,-227,-212,-199,-210,-223,-202,-222,-220,-196,-38,-197,-295,-238,-32,-201,-226,-198,-74,-236,-282,-275,-288,-27,-253,-54,-304,-244,-203,-232,-90,-87,-88,-235,-114,-69,-208,-207,-209,-59,-99,218,-231,-101,-63,-37,-241,-225,-196,-229,-89,-221,-39,-239,-33,-75,-85,-237,-97,304,-28,-55,-233,-277,-263,-270,-256,-284,-298,-291,-111,304,-115,-110,-109,-70,-71,-60,-100,-246,-250,-247,-253,-102,-64,-65,-280,-266,-273,-259,-287,-301,-294,-107,-251,-252,-253,-245,-278,-264,-271,-257,-285,-299,-292,-248,-249,-253,-279,-265,-258,-286,-300,-293,-272,-40,-34,-96,-303,-302,304,-76,-79,-78,-77,-98,-30,-276,-262,-269,-255,-283,-297,-290,-56,-91,-92,-112,304,-113,-72,-73,-219,-61,-146,-218,-66,-67,-108,-41,-35,-213,-80,-83,-82,-81,-31,-57,-214,-147,218,-148,304,-217,-149,-216,218,304,-215,]),'SSHORTSTRING':([2,4,6,7,16,19,22,28,30,32,40,44,45,47,59,70,84,87,90,91,92,93,94,95,96,102,109,111,117,130,136,137,138,139,140,141,142,143,144,145,146,150,156,157,159,161,162,169,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,283,284,285,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[40,40,-205,40,94,40,-206,40,-211,40,-209,40,-212,-210,40,40,40,40,40,40,-208,-207,-209,40,40,40,40,40,40,260,-46,-48,40,-49,-50,-45,-44,-47,-42,-43,-51,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,-52,40,-53,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,]),'DOUBLESTAR':([3,5,6,9,10,11,13,14,16,20,21,22,24,27,30,31,33,34,35,36,37,38,39,40,41,42,43,45,46,47,48,49,50,51,52,54,55,56,58,62,63,65,66,67,69,72,76,77,78,79,80,84,85,86,92,93,94,95,99,102,103,105,114,115,118,129,134,149,163,165,175,178,179,180,181,182,183,184,189,193,197,207,208,216,222,223,225,226,232,238,239,240,241,242,243,244,247,253,254,256,260,261,262,263,264,265,266,267,268,271,272,275,276,277,278,279,280,281,290,291,292,303,308,309,310,311,312,313,314,322,323,324,335,343,349,370,384,392,394,413,430,441,445,],[-296,-261,-205,-268,-260,-234,-200,-204,-94,-289,-93,-206,-254,-230,-211,-95,-240,-224,125,-243,-228,-274,-281,-209,-267,-242,-227,-212,-199,-210,-223,-202,-222,-220,-196,-197,-295,-238,-201,-226,-198,-236,-282,-275,-288,-253,-304,-244,-203,-232,176,192,-235,-114,-208,-207,-209,192,-99,192,-231,-101,-241,-225,-196,-229,-221,-239,-237,-97,-233,-277,-263,-270,-256,-284,-298,-291,-111,192,-115,-110,-109,-100,-246,-250,-247,-253,-102,-280,-266,-273,-259,-287,-301,-294,125,-251,-252,-253,-245,-278,-264,-271,-257,-285,-299,-292,-248,-249,-253,-279,-265,-258,-286,-300,-293,-272,-96,-303,-302,-98,-276,-262,-269,-255,-283,-297,-290,-112,192,192,-113,-219,-218,125,-213,-214,192,125,-217,-216,-215,]),'MINUS':([2,3,4,5,6,7,8,9,10,11,13,14,15,16,19,20,21,22,23,24,27,28,30,31,32,33,34,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,54,55,56,58,59,62,63,64,65,66,67,69,70,72,76,77,78,79,80,81,82,84,85,86,87,89,90,91,92,93,94,95,96,99,102,103,105,109,111,114,115,117,118,129,133,134,136,137,138,139,140,141,142,143,144,145,146,149,150,156,157,158,159,160,161,162,163,165,169,171,173,174,175,176,177,178,179,180,181,182,183,184,186,189,192,193,197,206,207,208,209,210,211,212,215,216,218,220,222,223,225,226,230,232,233,235,236,238,239,240,241,242,243,244,246,250,253,254,256,260,261,262,263,264,265,266,267,268,271,272,275,276,277,278,279,280,281,283,284,285,287,289,290,291,292,295,296,297,298,299,300,301,302,303,304,306,308,309,310,311,312,313,314,316,319,320,322,323,324,332,334,335,336,338,341,342,343,345,349,356,383,384,385,386,387,388,392,393,406,409,410,417,424,429,430,434,439,441,445,],[4,-296,4,-261,-205,4,-84,-268,-260,-234,-200,-204,91,-94,4,-289,-93,-206,-86,-254,-230,4,-211,-95,4,-240,-224,-243,-228,-274,-281,-209,-267,-242,-227,4,-212,-199,-210,-223,-202,-222,-220,-196,-197,-295,-238,-201,4,-226,-198,-74,-236,-282,-275,-288,4,-253,-304,-244,-203,-232,-90,-87,-88,4,-235,-114,4,210,4,4,-208,-207,-209,4,4,-99,4,-231,-101,4,4,-241,-225,4,-196,-229,-89,-221,-46,-48,4,-49,-50,-45,-44,-47,-42,-43,-51,-239,4,4,4,-75,4,-85,4,4,-237,-97,4,4,4,4,-233,4,4,-277,-263,-270,-256,-284,-298,-291,4,-111,4,4,-115,4,-110,-109,4,4,-70,-71,4,-100,4,4,-246,-250,-247,-253,4,-102,4,4,4,-280,-266,-273,-259,-287,-301,-294,4,4,-251,-252,-253,-245,-278,-264,-271,-257,-285,-299,-292,-248,-249,-253,-279,-265,-258,-286,-300,-293,-272,-52,4,-53,4,4,-96,-303,-302,-76,4,4,4,4,-79,-78,-77,-98,4,4,-276,-262,-269,-255,-283,-297,-290,4,-91,-92,-112,4,4,4,4,-113,4,4,-72,-73,-219,4,-218,4,4,-213,-80,-83,-82,-81,-214,4,4,4,4,4,4,4,-217,4,4,-216,-215,]),'DOT':([3,5,6,9,10,11,13,14,16,20,21,22,24,27,30,31,33,34,37,38,39,40,41,45,46,47,49,51,52,54,55,56,58,63,65,66,67,69,72,76,78,79,85,86,92,93,94,99,103,105,114,115,118,129,134,149,163,165,175,178,179,180,181,182,183,184,189,197,207,208,216,222,223,225,226,232,238,239,240,241,242,243,244,253,254,256,261,262,263,264,265,266,267,268,271,272,275,276,277,278,279,280,281,290,291,292,303,308,309,310,311,312,313,314,322,335,430,441,445,],[-296,-261,-205,-268,-260,88,-200,-204,-94,-289,-93,-206,-254,88,-211,-95,88,88,88,-274,-281,-209,-267,-212,-199,-210,-202,88,-196,-197,-295,88,-201,-198,88,-282,-275,-288,-253,-304,-203,88,88,-114,-208,-207,-209,-99,88,-101,88,88,-196,88,88,88,88,-97,88,-277,-263,-270,-256,-284,-298,-291,-111,-115,-110,-109,-100,-246,-250,-247,-253,-102,-280,-266,-273,-259,-287,-301,-294,-251,-252,-253,-278,-264,-271,-257,-285,-299,-292,-248,-249,-253,-279,-265,-258,-286,-300,-293,-272,-96,-303,-302,-98,-276,-262,-269,-255,-283,-297,-290,-112,-113,-217,-216,-215,]),'RIGHTSHIFT':([3,5,6,8,9,10,11,13,14,15,16,20,21,22,23,24,27,29,30,31,33,34,36,37,38,39,40,41,42,43,45,46,47,48,49,50,51,52,54,55,56,58,62,63,64,65,66,67,69,72,76,77,78,79,80,81,82,85,86,89,92,93,94,99,103,105,110,114,115,118,129,133,134,149,158,160,163,165,175,178,179,180,181,182,183,184,189,197,207,208,211,212,216,222,223,225,226,232,234,237,238,239,240,241,242,243,244,253,254,256,260,261,262,263,264,265,266,267,268,271,272,275,276,277,278,279,280,281,290,291,292,295,300,301,302,303,308,309,310,311,312,313,314,319,320,322,335,341,342,343,349,358,359,384,385,386,387,388,392,430,441,445,],[-296,-261,-205,-84,-268,-260,-234,-200,-204,-68,-94,-289,-93,-206,-86,-254,-230,111,-211,-95,-240,-224,-243,-228,-274,-281,-209,-267,-242,-227,-212,-199,-210,-223,-202,-222,-220,-196,-197,-295,-238,-201,-226,-198,-74,-236,-282,-275,-288,-253,-304,-244,-203,-232,-90,-87,-88,-235,-114,-69,-208,-207,-209,-99,-231,-101,236,-241,-225,-196,-229,-89,-221,-239,-75,-85,-237,-97,-233,-277,-263,-270,-256,-284,-298,-291,-111,-115,-110,-109,-70,-71,-100,-246,-250,-247,-253,-102,-64,-65,-280,-266,-273,-259,-287,-301,-294,-251,-252,-253,-245,-278,-264,-271,-257,-285,-299,-292,-248,-249,-253,-279,-265,-258,-286,-300,-293,-272,-96,-303,-302,-76,-79,-78,-77,-98,-276,-262,-269,-255,-283,-297,-290,-91,-92,-112,-113,-72,-73,-219,-218,-66,-67,-213,-80,-83,-82,-81,-214,-217,-216,-215,]),'LONGCOLUMNREFERENCE':([2,4,7,19,28,32,44,59,70,84,87,90,91,95,96,102,109,111,117,136,137,138,139,140,141,142,143,144,145,146,150,156,157,159,161,162,169,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,283,284,285,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,-46,-48,5,-49,-50,-45,-44,-47,-42,-43,-51,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,-52,5,-53,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,]),'GREATEREQUAL':([3,5,6,8,9,10,11,13,14,15,16,18,20,21,22,23,24,27,29,30,31,33,34,36,37,38,39,40,41,42,43,45,46,47,48,49,50,51,52,53,54,55,56,58,62,63,64,65,66,67,69,72,73,76,77,78,79,80,81,82,85,86,89,92,93,94,97,99,103,105,110,114,115,118,129,133,134,147,149,158,160,163,165,172,175,178,179,180,181,182,183,184,189,197,207,208,211,212,214,216,222,223,225,226,232,234,237,238,239,240,241,242,243,244,253,254,256,260,261,262,263,264,265,266,267,268,271,272,275,276,277,278,279,280,281,282,290,291,292,295,300,301,302,303,308,309,310,311,312,313,314,315,319,320,322,335,341,342,343,344,349,358,359,381,384,385,386,387,388,391,392,430,441,445,],[-296,-261,-205,-84,-268,-260,-234,-200,-204,-68,-94,-58,-289,-93,-206,-86,-254,-230,-62,-211,-95,-240,-224,-243,-228,-274,-281,-209,-267,-242,-227,-212,-199,-210,-223,-202,-222,-220,-196,136,-197,-295,-238,-201,-226,-198,-74,-236,-282,-275,-288,-253,-54,-304,-244,-203,-232,-90,-87,-88,-235,-114,-69,-208,-207,-209,-59,-99,-231,-101,-63,-241,-225,-196,-229,-89,-221,136,-239,-75,-85,-237,-97,-55,-233,-277,-263,-270,-256,-284,-298,-291,-111,-115,-110,-109,-70,-71,-60,-100,-246,-250,-247,-253,-102,-64,-65,-280,-266,-273,-259,-287,-301,-294,-251,-252,-253,-245,-278,-264,-271,-257,-285,-299,-292,-248,-249,-253,-279,-265,-258,-286,-300,-293,-272,-40,-96,-303,-302,-76,-79,-78,-77,-98,-276,-262,-269,-255,-283,-297,-290,-56,-91,-92,-112,-113,-72,-73,-219,-61,-218,-66,-67,-41,-213,-80,-83,-82,-81,-57,-214,-217,-216,-215,]),'GREATERTHAN':([3,5,6,8,9,10,11,13,14,15,16,18,20,21,22,23,24,27,29,30,31,33,34,36,37,38,39,40,41,42,43,45,46,47,48,49,50,51,52,53,54,55,56,58,62,63,64,65,66,67,69,72,73,76,77,78,79,80,81,82,85,86,89,92,93,94,97,99,103,105,110,114,115,118,129,133,134,147,149,154,155,158,160,163,165,172,175,178,179,180,181,182,183,184,189,197,207,208,211,212,214,216,222,223,225,226,232,234,237,238,239,240,241,242,243,244,253,254,256,260,261,262,263,264,265,266,267,268,271,272,275,276,277,278,279,280,281,282,290,291,292,295,300,301,302,303,308,309,310,311,312,313,314,315,319,320,322,335,341,342,343,344,349,358,359,381,384,385,386,387,388,391,392,430,441,445,],[-296,-261,-205,-84,-268,-260,-234,-200,-204,-68,-94,-58,-289,-93,-206,-86,-254,-230,-62,-211,-95,-240,-224,-243,-228,-274,-281,-209,-267,-242,-227,-212,-199,-210,-223,-202,-222,-220,-196,145,-197,-295,-238,-201,-226,-198,-74,-236,-282,-275,-288,-253,-54,-304,-244,-203,-232,-90,-87,-88,-235,-114,-69,-208,-207,-209,-59,-99,-231,-101,-63,-241,-225,-196,-229,-89,-221,145,-239,291,292,-75,-85,-237,-97,-55,-233,-277,-263,-270,-256,-284,-298,-291,-111,-115,-110,-109,-70,-71,-60,-100,-246,-250,-247,-253,-102,-64,-65,-280,-266,-273,-259,-287,-301,-294,-251,-252,-253,-245,-278,-264,-271,-257,-285,-299,-292,-248,-249,-253,-279,-265,-258,-286,-300,-293,-272,-40,-96,-303,-302,-76,-79,-78,-77,-98,-276,-262,-269,-255,-283,-297,-290,-56,-91,-92,-112,-113,-72,-73,-219,-61,-218,-66,-67,-41,-213,-80,-83,-82,-81,-57,-214,-217,-216,-215,]),'FLROWREFLIKENAME':([2,4,7,19,28,32,44,59,70,83,84,87,90,91,95,96,102,104,109,111,113,117,128,131,132,135,136,137,138,139,140,141,142,143,144,145,146,150,156,157,159,161,162,169,170,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,283,284,285,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[41,41,41,41,41,41,41,41,41,180,41,41,41,41,41,41,41,224,41,41,240,41,255,263,270,281,-46,-48,41,-49,-50,-45,-44,-47,-42,-43,-51,41,41,41,41,41,41,41,310,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,-52,41,-53,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,41,]),'EQUALTO':([3,5,6,8,9,10,11,13,14,15,16,18,20,21,22,23,24,27,29,30,31,33,34,36,37,38,39,40,41,42,43,45,46,47,48,49,50,51,52,53,54,55,56,58,62,63,64,65,66,67,69,72,73,76,77,78,79,80,81,82,85,86,89,92,93,94,97,99,103,105,110,114,115,118,129,133,134,147,149,158,160,163,165,172,175,178,179,180,181,182,183,184,189,197,207,208,211,212,214,216,222,223,225,226,232,234,237,238,239,240,241,242,243,244,253,254,256,260,261,262,263,264,265,266,267,268,271,272,275,276,277,278,279,280,281,282,290,291,292,295,300,301,302,303,308,309,310,311,312,313,314,315,319,320,322,335,341,342,343,344,349,358,359,381,384,385,386,387,388,391,392,430,441,445,],[-296,-261,-205,-84,-268,-260,-234,-200,-204,-68,-94,-58,-289,-93,-206,-86,-254,-230,-62,-211,-95,-240,-224,-243,-228,-274,-281,-209,-267,-242,-227,-212,-199,-210,-223,-202,-222,-220,-196,141,-197,-295,-238,-201,-226,-198,-74,-236,-282,-275,-288,-253,-54,-304,-244,-203,-232,-90,-87,-88,-235,-114,-69,-208,-207,-209,-59,-99,-231,-101,-63,-241,-225,-196,-229,-89,-221,141,-239,-75,-85,-237,-97,-55,-233,-277,-263,-270,-256,-284,-298,-291,-111,-115,-110,-109,-70,-71,-60,-100,-246,-250,-247,-253,-102,-64,-65,-280,-266,-273,-259,-287,-301,-294,-251,-252,-253,-245,-278,-264,-271,-257,-285,-299,-292,-248,-249,-253,-279,-265,-258,-286,-300,-293,-272,-40,-96,-303,-302,-76,-79,-78,-77,-98,-276,-262,-269,-255,-283,-297,-290,-56,-91,-92,-112,-113,-72,-73,-219,-61,-218,-66,-67,-41,-213,-80,-83,-82,-81,-57,-214,-217,-216,-215,]),'LONGNAMEDROWREFERENCE':([2,4,7,19,28,32,44,59,70,84,87,90,91,95,96,102,109,111,117,136,137,138,139,140,141,142,143,144,145,146,150,156,157,159,161,162,169,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,283,284,285,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,-46,-48,66,-49,-50,-45,-44,-47,-42,-43,-51,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,-52,66,-53,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,]),'VERTICALBAR':([3,5,6,8,9,10,11,13,14,15,16,18,20,21,22,23,24,27,29,30,31,33,34,36,37,38,39,40,41,42,43,45,46,47,48,49,50,51,52,54,55,56,58,62,63,64,65,66,67,69,72,73,76,77,78,79,80,81,82,85,86,89,92,93,94,97,99,103,105,110,114,115,118,129,133,134,149,158,160,163,165,172,175,178,179,180,181,182,183,184,189,197,207,208,211,212,214,216,222,223,225,226,232,234,237,238,239,240,241,242,243,244,253,254,256,260,261,262,263,264,265,266,267,268,271,272,275,276,277,278,279,280,281,290,291,292,295,300,301,302,303,308,309,310,311,312,313,314,315,319,320,322,335,341,342,343,344,349,358,359,384,385,386,387,388,391,392,430,441,445,],[-296,-261,-205,-84,-268,-260,-234,-200,-204,-68,-94,-58,-289,-93,-206,-86,-254,-230,-62,-211,-95,-240,-224,-243,-228,-274,-281,-209,-267,-242,-227,-212,-199,-210,-223,-202,-222,-220,-196,-197,-295,-238,-201,-226,-198,-74,-236,-282,-275,-288,-253,171,-304,-244,-203,-232,-90,-87,-88,-235,-114,-69,-208,-207,-209,-59,-99,-231,-101,-63,-241,-225,-196,-229,-89,-221,-239,-75,-85,-237,-97,316,-233,-277,-263,-270,-256,-284,-298,-291,-111,-115,-110,-109,-70,-71,-60,-100,-246,-250,-247,-253,-102,-64,-65,-280,-266,-273,-259,-287,-301,-294,-251,-252,-253,-245,-278,-264,-271,-257,-285,-299,-292,-248,-249,-253,-279,-265,-258,-286,-300,-293,-272,-96,-303,-302,-76,-79,-78,-77,-98,-276,-262,-269,-255,-283,-297,-290,-56,-91,-92,-112,-113,-72,-73,-219,-61,-218,-66,-67,-213,-80,-83,-82,-81,-57,-214,-217,-216,-215,]),'PLUS':([2,3,4,5,6,7,8,9,10,11,13,14,15,16,19,20,21,22,23,24,27,28,30,31,32,33,34,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,54,55,56,58,59,62,63,64,65,66,67,69,70,72,76,77,78,79,80,81,82,84,85,86,87,89,90,91,92,93,94,95,96,99,102,103,105,109,111,114,115,117,118,129,133,134,136,137,138,139,140,141,142,143,144,145,146,149,150,156,157,158,159,160,161,162,163,165,169,171,173,174,175,176,177,178,179,180,181,182,183,184,186,189,192,193,197,206,207,208,209,210,211,212,215,216,218,220,222,223,225,226,230,232,233,235,236,238,239,240,241,242,243,244,246,250,253,254,256,260,261,262,263,264,265,266,267,268,271,272,275,276,277,278,279,280,281,283,284,285,287,289,290,291,292,295,296,297,298,299,300,301,302,303,304,306,308,309,310,311,312,313,314,316,319,320,322,323,324,332,334,335,336,338,341,342,343,345,349,356,383,384,385,386,387,388,392,393,406,409,410,417,424,429,430,434,439,441,445,],[7,-296,7,-261,-205,7,-84,-268,-260,-234,-200,-204,90,-94,7,-289,-93,-206,-86,-254,-230,7,-211,-95,7,-240,-224,-243,-228,-274,-281,-209,-267,-242,-227,7,-212,-199,-210,-223,-202,-222,-220,-196,-197,-295,-238,-201,7,-226,-198,-74,-236,-282,-275,-288,7,-253,-304,-244,-203,-232,-90,-87,-88,7,-235,-114,7,209,7,7,-208,-207,-209,7,7,-99,7,-231,-101,7,7,-241,-225,7,-196,-229,-89,-221,-46,-48,7,-49,-50,-45,-44,-47,-42,-43,-51,-239,7,7,7,-75,7,-85,7,7,-237,-97,7,7,7,7,-233,7,7,-277,-263,-270,-256,-284,-298,-291,7,-111,7,7,-115,7,-110,-109,7,7,-70,-71,7,-100,7,7,-246,-250,-247,-253,7,-102,7,7,7,-280,-266,-273,-259,-287,-301,-294,7,7,-251,-252,-253,-245,-278,-264,-271,-257,-285,-299,-292,-248,-249,-253,-279,-265,-258,-286,-300,-293,-272,-52,7,-53,7,7,-96,-303,-302,-76,7,7,7,7,-79,-78,-77,-98,7,7,-276,-262,-269,-255,-283,-297,-290,7,-91,-92,-112,7,7,7,7,-113,7,7,-72,-73,-219,7,-218,7,7,-213,-80,-83,-82,-81,-214,7,7,7,7,7,7,7,-217,7,7,-216,-215,]),'LEFTBRACKET':([2,3,4,5,6,7,9,10,11,13,14,16,19,20,21,22,24,27,28,30,31,32,33,34,37,38,39,40,41,44,45,46,47,49,51,52,54,55,56,58,59,63,65,66,67,69,70,72,76,78,79,84,85,86,87,90,91,92,93,94,95,96,99,102,103,105,109,111,114,115,117,118,129,134,136,137,138,139,140,141,142,143,144,145,146,149,150,156,157,159,161,162,163,165,169,171,173,174,175,176,177,178,179,180,181,182,183,184,186,189,192,193,197,206,207,208,209,210,215,216,218,220,222,223,225,226,230,232,233,235,236,238,239,240,241,242,243,244,246,250,253,254,256,261,262,263,264,265,266,267,268,271,272,275,276,277,278,279,280,281,283,284,285,287,289,290,291,292,296,297,298,299,303,304,306,308,309,310,311,312,313,314,316,322,323,324,332,334,335,336,338,345,356,383,393,406,409,410,417,424,429,430,434,439,441,445,],[19,-296,19,-261,-205,19,-268,-260,87,-200,-204,-94,19,-289,-93,-206,-254,87,19,-211,-95,19,87,87,87,-274,-281,-209,-267,19,-212,-199,-210,-202,87,-196,-197,-295,87,-201,19,-198,87,-282,-275,-288,19,-253,-304,-203,87,19,87,-114,19,19,19,-208,-207,-209,19,19,-99,19,87,-101,19,19,87,87,19,-196,87,87,-46,-48,19,-49,-50,-45,-44,-47,-42,-43,-51,87,19,19,19,19,19,19,87,-97,19,19,19,19,87,19,19,-277,-263,-270,-256,-284,-298,-291,19,-111,19,19,-115,19,-110,-109,19,19,19,-100,19,19,-246,-250,-247,-253,19,-102,19,19,19,-280,-266,-273,-259,-287,-301,-294,19,19,-251,-252,-253,-278,-264,-271,-257,-285,-299,-292,-248,-249,-253,-279,-265,-258,-286,-300,-293,-272,-52,19,-53,19,19,-96,-303,-302,19,19,19,19,-98,19,19,-276,-262,-269,-255,-283,-297,-290,19,-112,19,19,19,19,-113,19,19,19,19,19,19,19,19,19,19,19,19,-217,19,19,-216,-215,]),'TILDE':([2,4,7,19,28,32,44,59,70,84,87,90,91,95,96,102,109,111,117,136,137,138,139,140,141,142,143,144,145,146,150,156,157,159,161,162,169,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,283,284,285,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,-46,-48,44,-49,-50,-45,-44,-47,-42,-43,-51,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,-52,44,-53,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,]),'LONGDELETEDREFERENCE':([2,4,7,19,28,32,44,59,70,84,87,90,91,95,96,102,104,109,111,117,132,136,137,138,139,140,141,142,143,144,145,146,150,156,157,159,161,162,169,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,283,284,285,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,-46,-48,20,-49,-50,-45,-44,-47,-42,-43,-51,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,-52,20,-53,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,]),'COLON':([3,20,24,27,36,42,55,69,72,181,183,184,241,243,244,264,266,267,277,279,280,311,313,314,],[-296,-289,-254,104,128,132,-295,-288,-253,-256,-298,-291,-259,-301,-294,-257,-299,-292,-258,-300,-293,-255,-297,-290,]),'FLDELETEDREFERENCE':([2,4,7,19,28,32,44,59,70,83,84,87,90,91,95,96,102,104,109,111,113,117,131,132,135,136,137,138,139,140,141,142,143,144,145,146,150,156,157,159,161,162,169,170,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,283,284,285,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,350,351,352,353,354,356,376,377,378,379,380,383,393,406,409,410,417,424,429,434,439,],[69,69,69,69,69,69,69,69,69,184,69,69,69,69,69,69,69,69,69,69,244,69,267,69,280,-46,-48,69,-49,-50,-45,-44,-47,-42,-43,-51,69,69,69,69,69,69,69,314,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,-52,69,-53,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,267,314,244,184,280,69,184,267,314,244,280,69,69,69,69,69,69,69,69,69,69,]),'LITTLEARROW':([3,5,6,8,9,10,11,12,13,14,15,16,18,20,21,22,23,24,26,27,29,30,31,33,34,35,36,37,38,39,40,41,42,43,45,46,47,48,49,50,51,52,53,54,55,56,57,58,62,63,64,65,66,67,69,71,72,73,76,77,78,79,80,81,82,85,86,87,89,92,93,94,97,99,103,105,108,110,112,114,115,116,118,119,120,122,123,124,127,129,133,134,147,149,151,158,160,163,165,168,172,175,178,179,180,181,182,183,184,189,197,198,200,205,206,207,208,211,212,214,216,222,223,225,226,232,234,237,238,239,240,241,242,243,244,245,247,251,252,253,254,256,260,261,262,263,264,265,266,267,268,271,272,275,276,277,278,279,280,281,282,286,290,291,292,295,300,301,302,303,307,308,309,310,311,312,313,314,315,319,320,322,335,336,338,339,340,341,342,343,344,349,358,359,360,361,362,363,364,365,368,369,381,382,384,385,386,387,388,390,391,392,405,406,413,416,425,426,430,441,445,],[-296,-261,-205,-84,-268,-260,-234,-29,-200,-204,-68,-94,-58,-289,-93,-206,-86,-254,-36,-230,-62,-211,-95,-240,-224,117,-243,-228,-274,-281,-209,-267,-242,-227,-212,-199,-210,-223,-202,-222,-220,-196,-38,-197,-295,-238,-32,-201,-226,-198,-74,-236,-282,-275,-288,-27,-253,-54,-304,-244,-203,-232,-90,-87,-88,-235,-114,206,-69,-208,-207,-209,-59,-99,-231,-101,233,-63,-37,-241,-225,-3,-196,-13,-19,-2,250,-5,-4,-229,-89,-221,-39,-239,-33,-75,-85,-237,-97,-28,-55,-233,-277,-263,-270,-256,-284,-298,-291,-111,-115,334,336,-132,-128,-110,-109,-70,-71,-60,-100,-246,-250,-247,-253,-102,-64,-65,-280,-266,-273,-259,-287,-301,-294,-107,-14,-6,-7,-251,-252,-253,-245,-278,-264,-271,-257,-285,-299,-292,-248,-249,-253,-279,-265,-258,-286,-300,-293,-272,-40,-34,-96,-303,-302,-76,-79,-78,-77,-98,-30,-276,-262,-269,-255,-283,-297,-290,-56,-91,-92,-112,-113,-129,206,-130,-133,-72,-73,-219,-61,-218,-66,-67,-9,-17,-13,-18,-15,-16,-20,-108,-41,-35,-213,-80,-83,-82,-81,-31,-57,-214,-131,206,-10,-8,-12,-11,-217,-216,-215,]),'LONGROWREFERENCE':([2,4,7,19,28,32,44,59,70,84,87,90,91,95,96,102,109,111,117,136,137,138,139,140,141,142,143,144,145,146,150,156,157,159,161,162,169,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,283,284,285,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,-46,-48,9,-49,-50,-45,-44,-47,-42,-43,-51,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,-52,9,-53,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,]),'$end':([1,3,5,6,8,9,10,11,12,13,14,15,16,18,20,21,22,23,24,26,27,29,30,31,33,34,36,37,38,39,40,41,42,43,45,46,47,48,49,50,51,52,53,54,55,56,57,58,62,63,64,65,66,67,68,69,71,72,73,76,77,78,79,80,81,82,85,86,89,92,93,94,97,99,103,105,110,112,114,115,118,129,133,134,147,149,151,158,160,163,165,168,172,175,178,179,180,181,182,183,184,189,197,207,208,211,212,214,216,222,223,225,226,232,234,237,238,239,240,241,242,243,244,245,253,254,256,260,261,262,263,264,265,266,267,268,271,272,275,276,277,278,279,280,281,282,286,290,291,292,295,300,301,302,303,307,308,309,310,311,312,313,314,315,319,320,322,335,341,342,343,344,349,358,359,369,381,382,384,385,386,387,388,390,391,392,430,441,445,],[0,-296,-261,-205,-84,-268,-260,-234,-29,-200,-204,-68,-94,-58,-289,-93,-206,-86,-254,-36,-230,-62,-211,-95,-240,-224,-243,-228,-274,-281,-209,-267,-242,-227,-212,-199,-210,-223,-202,-222,-220,-196,-38,-197,-295,-238,-32,-201,-226,-198,-74,-236,-282,-275,-1,-288,-27,-253,-54,-304,-244,-203,-232,-90,-87,-88,-235,-114,-69,-208,-207,-209,-59,-99,-231,-101,-63,-37,-241,-225,-196,-229,-89,-221,-39,-239,-33,-75,-85,-237,-97,-28,-55,-233,-277,-263,-270,-256,-284,-298,-291,-111,-115,-110,-109,-70,-71,-60,-100,-246,-250,-247,-253,-102,-64,-65,-280,-266,-273,-259,-287,-301,-294,-107,-251,-252,-253,-245,-278,-264,-271,-257,-285,-299,-292,-248,-249,-253,-279,-265,-258,-286,-300,-293,-272,-40,-34,-96,-303,-302,-76,-79,-78,-77,-98,-30,-276,-262,-269,-255,-283,-297,-290,-56,-91,-92,-112,-113,-72,-73,-219,-61,-218,-66,-67,-108,-41,-35,-213,-80,-83,-82,-81,-31,-57,-214,-217,-216,-215,]),'DSHORTSTRING':([2,4,6,7,16,19,22,28,30,32,40,44,45,47,59,70,84,87,90,91,92,93,94,95,96,102,109,111,117,136,137,138,139,140,141,142,143,144,145,146,150,156,157,159,161,162,169,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,283,284,285,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[47,47,-205,47,47,47,-206,47,-211,47,-209,47,-212,-210,47,47,47,47,47,47,-208,-207,-209,47,47,47,47,47,47,-46,-48,47,-49,-50,-45,-44,-47,-42,-43,-51,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,-52,47,-53,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,]),'LONGNAMEDCOLUMNREFERENCE':([2,4,7,19,28,32,44,59,70,84,87,90,91,95,96,102,109,111,117,136,137,138,139,140,141,142,143,144,145,146,150,156,157,159,161,162,169,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,283,284,285,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,-46,-48,67,-49,-50,-45,-44,-47,-42,-43,-51,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,-52,67,-53,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,67,]),'LEFTBRACE':([2,4,7,19,28,32,44,59,70,84,87,90,91,95,96,102,109,111,117,136,137,138,139,140,141,142,143,144,145,146,150,156,157,159,161,162,169,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,283,284,285,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,-46,-48,28,-49,-50,-45,-44,-47,-42,-43,-51,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,-52,28,-53,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,]),'FLCOLUMNREFLIKENAME':([2,4,7,19,28,32,44,59,70,83,84,87,90,91,95,96,102,104,109,111,113,117,128,131,132,135,136,137,138,139,140,141,142,143,144,145,146,150,156,157,159,161,162,169,170,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,283,284,285,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[10,10,10,10,10,10,10,10,10,179,10,10,10,10,10,10,10,228,10,10,239,10,258,262,269,276,-46,-48,10,-49,-50,-45,-44,-47,-42,-43,-51,10,10,10,10,10,10,10,309,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,-52,10,-53,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,]),'OCTINTEGER':([2,4,7,19,28,32,44,59,70,84,87,90,91,95,96,102,109,111,117,136,137,138,139,140,141,142,143,144,145,146,150,156,157,159,161,162,169,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,283,284,285,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,-46,-48,49,-49,-50,-45,-44,-47,-42,-43,-51,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,-52,49,-53,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,]),'ISERROR':([2,4,7,19,28,32,44,59,70,84,87,90,91,95,96,102,109,111,117,136,137,138,139,140,141,142,143,144,145,146,150,156,157,159,161,162,169,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,283,284,285,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,-46,-48,61,-49,-50,-45,-44,-47,-42,-43,-51,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,-52,61,-53,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,]),'PERCENT':([3,5,6,8,9,10,11,13,14,16,20,21,22,23,24,27,30,31,33,34,36,37,38,39,40,41,42,43,45,46,47,48,49,50,51,52,54,55,56,58,62,63,64,65,66,67,69,72,76,77,78,79,80,81,82,85,86,92,93,94,99,103,105,114,115,118,129,133,134,149,160,163,165,175,178,179,180,181,182,183,184,189,197,207,208,216,222,223,225,226,232,238,239,240,241,242,243,244,253,254,256,260,261,262,263,264,265,266,267,268,271,272,275,276,277,278,279,280,281,290,291,292,295,300,301,302,303,308,309,310,311,312,313,314,319,320,322,335,343,349,384,385,386,387,388,392,430,441,445,],[-296,-261,-205,-84,-268,-260,-234,-200,-204,-94,-289,-93,-206,-86,-254,-230,-211,-95,-240,-224,-243,-228,-274,-281,-209,-267,-242,-227,-212,-199,-210,-223,-202,-222,-220,-196,-197,-295,-238,-201,-226,-198,160,-236,-282,-275,-288,-253,-304,-244,-203,-232,-90,-87,-88,-235,-114,-208,-207,-209,-99,-231,-101,-241,-225,-196,-229,-89,-221,-239,-85,-237,-97,-233,-277,-263,-270,-256,-284,-298,-291,-111,-115,-110,-109,-100,-246,-250,-247,-253,-102,-280,-266,-273,-259,-287,-301,-294,-251,-252,-253,-245,-278,-264,-271,-257,-285,-299,-292,-248,-249,-253,-279,-265,-258,-286,-300,-293,-272,-96,-303,-302,160,160,160,160,-98,-276,-262,-269,-255,-283,-297,-290,-91,-92,-112,-113,-219,-218,-213,160,160,160,160,-214,-217,-216,-215,]),'IS':([3,5,6,8,9,10,11,13,14,15,16,18,20,21,22,23,24,27,29,30,31,33,34,36,37,38,39,40,41,42,43,45,46,47,48,49,50,51,52,53,54,55,56,58,62,63,64,65,66,67,69,72,73,76,77,78,79,80,81,82,85,86,89,92,93,94,97,99,103,105,110,114,115,118,129,133,134,147,149,158,160,163,165,172,175,178,179,180,181,182,183,184,189,197,207,208,211,212,214,216,222,223,225,226,232,234,237,238,239,240,241,242,243,244,253,254,256,260,261,262,263,264,265,266,267,268,271,272,275,276,277,278,279,280,281,282,290,291,292,295,300,301,302,303,308,309,310,311,312,313,314,315,319,320,322,335,341,342,343,344,349,358,359,381,384,385,386,387,388,391,392,430,441,445,],[-296,-261,-205,-84,-268,-260,-234,-200,-204,-68,-94,-58,-289,-93,-206,-86,-254,-230,-62,-211,-95,-240,-224,-243,-228,-274,-281,-209,-267,-242,-227,-212,-199,-210,-223,-202,-222,-220,-196,140,-197,-295,-238,-201,-226,-198,-74,-236,-282,-275,-288,-253,-54,-304,-244,-203,-232,-90,-87,-88,-235,-114,-69,-208,-207,-209,-59,-99,-231,-101,-63,-241,-225,-196,-229,-89,-221,140,-239,-75,-85,-237,-97,-55,-233,-277,-263,-270,-256,-284,-298,-291,-111,-115,-110,-109,-70,-71,-60,-100,-246,-250,-247,-253,-102,-64,-65,-280,-266,-273,-259,-287,-301,-294,-251,-252,-253,-245,-278,-264,-271,-257,-285,-299,-292,-248,-249,-253,-279,-265,-258,-286,-300,-293,-272,-40,-96,-303,-302,-76,-79,-78,-77,-98,-276,-262,-269,-255,-283,-297,-290,-56,-91,-92,-112,-113,-72,-73,-219,-61,-218,-66,-67,-41,-213,-80,-83,-82,-81,-57,-214,-217,-216,-215,]),'LEFTPAREN':([2,3,4,5,6,7,9,10,11,13,14,16,17,19,20,21,22,24,25,27,28,30,31,32,33,34,35,37,38,39,40,41,44,45,46,47,49,51,52,54,55,56,58,59,61,63,65,66,67,69,70,72,74,75,76,78,79,84,85,86,87,90,91,92,93,94,95,96,99,102,103,105,109,111,114,115,117,118,121,129,134,136,137,138,139,140,141,142,143,144,145,146,149,150,156,157,159,161,162,163,165,169,171,173,174,175,176,177,178,179,180,181,182,183,184,186,189,192,193,197,206,207,208,209,210,215,216,218,220,222,223,225,226,230,232,233,235,236,238,239,240,241,242,243,244,246,247,250,253,254,256,261,262,263,264,265,266,267,268,271,272,275,276,277,278,279,280,281,283,284,285,287,289,290,291,292,296,297,298,299,303,304,306,308,309,310,311,312,313,314,316,322,323,324,332,334,335,336,338,345,356,367,383,393,406,409,410,414,417,424,429,430,434,439,441,445,],[70,-296,70,-261,-205,70,-268,-260,84,-200,-204,-94,95,70,-289,-93,-206,-254,102,84,70,-211,-95,70,84,84,121,84,-274,-281,-209,-267,70,-212,-199,-210,-202,84,-196,-197,-295,84,-201,70,156,-198,84,-282,-275,-288,70,-253,173,174,-304,-203,84,70,84,-114,70,70,70,-208,-207,-209,70,70,-99,70,84,-101,70,70,84,84,70,-196,121,84,84,-46,-48,70,-49,-50,-45,-44,-47,-42,-43,-51,84,70,70,70,70,70,70,84,-97,70,70,70,70,84,70,70,-277,-263,-270,-256,-284,-298,-291,70,-111,70,70,-115,70,-110,-109,70,70,70,-100,70,70,-246,-250,-247,-253,70,-102,70,70,70,-280,-266,-273,-259,-287,-301,-294,70,121,70,-251,-252,-253,-278,-264,-271,-257,-285,-299,-292,-248,-249,-253,-279,-265,-258,-286,-300,-293,-272,-52,70,-53,70,70,-96,-303,-302,70,70,70,70,-98,70,70,-276,-262,-269,-255,-283,-297,-290,70,-112,70,70,70,70,-113,70,70,70,70,121,70,70,70,70,70,121,70,70,70,-217,70,70,-216,-215,]),'EQUALS':([0,3,5,6,8,9,10,11,13,14,15,16,18,20,21,22,23,24,27,29,30,31,33,34,36,37,38,39,40,41,42,43,45,46,47,48,49,50,51,52,53,54,55,56,58,62,63,64,65,66,67,69,72,73,76,77,78,79,80,81,82,85,86,89,92,93,94,97,99,103,105,110,114,115,118,129,133,134,147,149,158,160,163,165,172,175,178,179,180,181,182,183,184,189,197,207,208,211,212,214,216,222,223,225,226,232,234,237,238,239,240,241,242,243,244,253,254,256,260,261,262,263,264,265,266,267,268,271,272,275,276,277,278,279,280,281,282,290,291,292,295,300,301,302,303,308,309,310,311,312,313,314,315,319,320,322,335,341,342,343,344,349,358,359,381,384,385,386,387,388,391,392,430,441,445,],[2,-296,-261,-205,-84,-268,-260,-234,-200,-204,-68,-94,-58,-289,-93,-206,-86,-254,-230,-62,-211,-95,-240,-224,-243,-228,-274,-281,-209,-267,-242,-227,-212,-199,-210,-223,-202,-222,-220,-196,142,-197,-295,-238,-201,-226,-198,-74,-236,-282,-275,-288,-253,-54,-304,-244,-203,-232,-90,-87,-88,-235,-114,-69,-208,-207,-209,-59,-99,-231,-101,-63,-241,-225,-196,-229,-89,-221,142,-239,-75,-85,-237,-97,-55,-233,-277,-263,-270,-256,-284,-298,-291,-111,-115,-110,-109,-70,-71,-60,-100,-246,-250,-247,-253,-102,-64,-65,-280,-266,-273,-259,-287,-301,-294,-251,-252,-253,-245,-278,-264,-271,-257,-285,-299,-292,-248,-249,-253,-279,-265,-258,-286,-300,-293,-272,-40,-96,-303,-302,-76,-79,-78,-77,-98,-276,-262,-269,-255,-283,-297,-290,-56,-91,-92,-112,-113,-72,-73,-219,-61,-218,-66,-67,-41,-213,-80,-83,-82,-81,-57,-214,-217,-216,-215,]),'ELLIPSIS':([87,338,406,],[204,204,204,]),'DOUBLESLASH':([3,5,6,8,9,10,11,13,14,16,20,21,22,23,24,27,30,31,33,34,36,37,38,39,40,41,42,43,45,46,47,48,49,50,51,52,54,55,56,58,62,63,64,65,66,67,69,72,76,77,78,79,80,81,82,85,86,92,93,94,99,103,105,114,115,118,129,133,134,149,158,160,163,165,175,178,179,180,181,182,183,184,189,197,207,208,216,222,223,225,226,232,238,239,240,241,242,243,244,253,254,256,260,261,262,263,264,265,266,267,268,271,272,275,276,277,278,279,280,281,290,291,292,295,300,301,302,303,308,309,310,311,312,313,314,319,320,322,335,343,349,384,385,386,387,388,392,430,441,445,],[-296,-261,-205,-84,-268,-260,-234,-200,-204,-94,-289,-93,-206,-86,-254,-230,-211,-95,-240,-224,-243,-228,-274,-281,-209,-267,-242,-227,-212,-199,-210,-223,-202,-222,-220,-196,-197,-295,-238,-201,-226,-198,161,-236,-282,-275,-288,-253,-304,-244,-203,-232,-90,-87,-88,-235,-114,-208,-207,-209,-99,-231,-101,-241,-225,-196,-229,-89,-221,-239,298,-85,-237,-97,-233,-277,-263,-270,-256,-284,-298,-291,-111,-115,-110,-109,-100,-246,-250,-247,-253,-102,-280,-266,-273,-259,-287,-301,-294,-251,-252,-253,-245,-278,-264,-271,-257,-285,-299,-292,-248,-249,-253,-279,-265,-258,-286,-300,-293,-272,-96,-303,-302,-76,-79,-78,-77,-98,-276,-262,-269,-255,-283,-297,-290,-91,-92,-112,-113,-219,-218,-213,-80,-83,-82,-81,-214,-217,-216,-215,]),'LONGCELLREFERENCE':([2,4,7,19,28,32,44,59,70,84,87,90,91,95,96,102,104,109,111,117,128,132,136,137,138,139,140,141,142,143,144,145,146,150,156,157,159,161,162,169,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,283,284,285,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,-46,-48,24,-49,-50,-45,-44,-47,-42,-43,-51,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,-52,24,-53,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,]),'NOT':([2,3,5,6,8,9,10,11,13,14,15,16,18,19,20,21,22,23,24,27,28,29,30,31,32,33,34,36,37,38,39,40,41,42,43,45,46,47,48,49,50,51,52,53,54,55,56,58,59,62,63,64,65,66,67,69,70,72,73,76,77,78,79,80,81,82,84,85,86,87,89,92,93,94,95,97,99,102,103,105,110,114,115,117,118,129,133,134,140,147,149,150,156,158,160,163,165,169,172,173,174,175,178,179,180,181,182,183,184,186,189,192,193,197,206,207,208,211,212,214,216,220,222,223,225,226,230,232,233,234,237,238,239,240,241,242,243,244,246,250,253,254,256,260,261,262,263,264,265,266,267,268,271,272,275,276,277,278,279,280,281,282,287,289,290,291,292,295,300,301,302,303,306,308,309,310,311,312,313,314,315,319,320,322,323,324,332,334,335,336,338,341,342,343,344,345,349,356,358,359,381,383,384,385,386,387,388,391,392,393,406,409,417,429,430,434,439,441,445,],[32,-296,-261,-205,-84,-268,-260,-234,-200,-204,-68,-94,-58,32,-289,-93,-206,-86,-254,-230,32,-62,-211,-95,32,-240,-224,-243,-228,-274,-281,-209,-267,-242,-227,-212,-199,-210,-223,-202,-222,-220,-196,148,-197,-295,-238,-201,32,-226,-198,-74,-236,-282,-275,-288,32,-253,-54,-304,-244,-203,-232,-90,-87,-88,32,-235,-114,32,-69,-208,-207,-209,32,-59,-99,32,-231,-101,-63,-241,-225,32,-196,-229,-89,-221,283,148,-239,32,32,-75,-85,-237,-97,32,-55,32,32,-233,-277,-263,-270,-256,-284,-298,-291,32,-111,32,32,-115,32,-110,-109,-70,-71,-60,-100,32,-246,-250,-247,-253,32,-102,32,-64,-65,-280,-266,-273,-259,-287,-301,-294,32,32,-251,-252,-253,-245,-278,-264,-271,-257,-285,-299,-292,-248,-249,-253,-279,-265,-258,-286,-300,-293,-272,-40,32,32,-96,-303,-302,-76,-79,-78,-77,-98,32,-276,-262,-269,-255,-283,-297,-290,-56,-91,-92,-112,32,32,32,32,-113,32,32,-72,-73,-219,-61,32,-218,32,-66,-67,-41,32,-213,-80,-83,-82,-81,-57,-214,32,32,32,32,32,-217,32,32,-216,-215,]),'AMPERSAND':([3,5,6,8,9,10,11,13,14,15,16,18,20,21,22,23,24,27,29,30,31,33,34,36,37,38,39,40,41,42,43,45,46,47,48,49,50,51,52,54,55,56,58,62,63,64,65,66,67,69,72,76,77,78,79,80,81,82,85,86,89,92,93,94,97,99,103,105,110,114,115,118,129,133,134,149,158,160,163,165,175,178,179,180,181,182,183,184,189,197,207,208,211,212,214,216,222,223,225,226,232,234,237,238,239,240,241,242,243,244,253,254,256,260,261,262,263,264,265,266,267,268,271,272,275,276,277,278,279,280,281,290,291,292,295,300,301,302,303,308,309,310,311,312,313,314,319,320,322,335,341,342,343,344,349,358,359,384,385,386,387,388,392,430,441,445,],[-296,-261,-205,-84,-268,-260,-234,-200,-204,-68,-94,96,-289,-93,-206,-86,-254,-230,-62,-211,-95,-240,-224,-243,-228,-274,-281,-209,-267,-242,-227,-212,-199,-210,-223,-202,-222,-220,-196,-197,-295,-238,-201,-226,-198,-74,-236,-282,-275,-288,-253,-304,-244,-203,-232,-90,-87,-88,-235,-114,-69,-208,-207,-209,215,-99,-231,-101,-63,-241,-225,-196,-229,-89,-221,-239,-75,-85,-237,-97,-233,-277,-263,-270,-256,-284,-298,-291,-111,-115,-110,-109,-70,-71,-60,-100,-246,-250,-247,-253,-102,-64,-65,-280,-266,-273,-259,-287,-301,-294,-251,-252,-253,-245,-278,-264,-271,-257,-285,-299,-292,-248,-249,-253,-279,-265,-258,-286,-300,-293,-272,-96,-303,-302,-76,-79,-78,-77,-98,-276,-262,-269,-255,-283,-297,-290,-91,-92,-112,-113,-72,-73,-219,-61,-218,-66,-67,-213,-80,-83,-82,-81,-214,-217,-216,-215,]),'CIRCUMFLEX':([3,5,6,9,10,11,13,14,16,20,21,22,24,27,30,31,33,34,36,37,38,39,40,41,42,43,45,46,47,48,49,50,51,52,54,55,56,58,62,63,65,66,67,69,72,76,77,78,79,80,85,86,92,93,94,99,103,105,114,115,118,129,134,149,163,165,175,178,179,180,181,182,183,184,189,197,207,208,216,222,223,225,226,232,238,239,240,241,242,243,244,253,254,256,260,261,262,263,264,265,266,267,268,271,272,275,276,277,278,279,280,281,290,291,292,303,308,309,310,311,312,313,314,322,335,343,349,384,392,430,441,445,],[-296,-261,-205,-268,-260,-234,-200,-204,-94,-289,-93,-206,-254,-230,-211,-95,-240,-224,-243,-228,-274,-281,-209,-267,-242,-227,-212,-199,-210,-223,-202,-222,-220,-196,-197,-295,-238,-201,-226,-198,-236,-282,-275,-288,-253,-304,-244,-203,-232,177,-235,-114,-208,-207,-209,-99,-231,-101,-241,-225,-196,-229,-221,-239,-237,-97,-233,-277,-263,-270,-256,-284,-298,-291,-111,-115,-110,-109,-100,-246,-250,-247,-253,-102,-280,-266,-273,-259,-287,-301,-294,-251,-252,-253,-245,-278,-264,-271,-257,-285,-299,-292,-248,-249,-253,-279,-265,-258,-286,-300,-293,-272,-96,-303,-302,-98,-276,-262,-269,-255,-283,-297,-290,-112,-113,-219,-218,-213,-214,-217,-216,-215,]),'IN':([3,5,6,8,9,10,11,13,14,15,16,18,20,21,22,23,24,27,29,30,31,33,34,36,37,38,39,40,41,42,43,45,46,47,48,49,50,51,52,53,54,55,56,58,62,63,64,65,66,67,69,72,73,76,77,78,79,80,81,82,85,86,89,92,93,94,97,99,103,105,110,114,115,118,129,133,134,147,148,149,158,160,163,165,172,175,178,179,180,181,182,183,184,189,197,207,208,211,212,214,216,222,223,225,226,232,234,237,238,239,240,241,242,243,244,253,254,256,260,261,262,263,264,265,266,267,268,271,272,275,276,277,278,279,280,281,282,290,291,292,295,300,301,302,303,308,309,310,311,312,313,314,315,319,320,322,335,341,342,343,344,346,347,349,358,359,381,384,385,386,387,388,389,391,392,410,411,423,424,430,436,441,445,],[-296,-261,-205,-84,-268,-260,-234,-200,-204,-68,-94,-58,-289,-93,-206,-86,-254,-230,-62,-211,-95,-240,-224,-243,-228,-274,-281,-209,-267,-242,-227,-212,-199,-210,-223,-202,-222,-220,-196,146,-197,-295,-238,-201,-226,-198,-74,-236,-282,-275,-288,-253,-54,-304,-244,-203,-232,-90,-87,-88,-235,-114,-69,-208,-207,-209,-59,-99,-231,-101,-63,-241,-225,-196,-229,-89,-221,146,285,-239,-75,-85,-237,-97,-55,-233,-277,-263,-270,-256,-284,-298,-291,-111,-115,-110,-109,-70,-71,-60,-100,-246,-250,-247,-253,-102,-64,-65,-280,-266,-273,-259,-287,-301,-294,-251,-252,-253,-245,-278,-264,-271,-257,-285,-299,-292,-248,-249,-253,-279,-265,-258,-286,-300,-293,-272,-40,-96,-303,-302,-76,-79,-78,-77,-98,-276,-262,-269,-255,-283,-297,-290,-56,-91,-92,-112,-113,-72,-73,-219,-61,409,-136,-218,-66,-67,-41,-213,-80,-83,-82,-81,417,-57,-214,-137,-138,-140,-139,-217,-141,-216,-215,]),'ISERR':([2,4,7,19,28,32,44,59,70,84,87,90,91,95,96,102,109,111,117,136,137,138,139,140,141,142,143,144,145,146,150,156,157,159,161,162,169,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,283,284,285,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,-46,-48,74,-49,-50,-45,-44,-47,-42,-43,-51,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,-52,74,-53,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,]),'RIGHTBRACE':([3,5,6,8,9,10,11,12,13,14,15,16,18,20,21,22,23,24,26,27,28,29,30,31,33,34,36,37,38,39,40,41,42,43,45,46,47,48,49,50,51,52,53,54,55,56,57,58,62,63,64,65,66,67,69,71,72,73,76,77,78,79,80,81,82,85,86,89,92,93,94,97,99,103,105,106,107,110,112,114,115,118,129,133,134,147,149,151,158,160,163,165,168,172,175,178,179,180,181,182,183,184,189,197,207,208,211,212,214,216,222,223,225,226,230,231,232,234,237,238,239,240,241,242,243,244,245,253,254,256,260,261,262,263,264,265,266,267,268,271,272,275,276,277,278,279,280,281,282,286,290,291,292,295,300,301,302,303,307,308,309,310,311,312,313,314,315,319,320,322,335,341,342,343,344,349,355,356,357,358,359,369,381,382,384,385,386,387,388,390,391,392,412,430,441,445,],[-296,-261,-205,-84,-268,-260,-234,-29,-200,-204,-68,-94,-58,-289,-93,-206,-86,-254,-36,-230,105,-62,-211,-95,-240,-224,-243,-228,-274,-281,-209,-267,-242,-227,-212,-199,-210,-223,-202,-222,-220,-196,-38,-197,-295,-238,-32,-201,-226,-198,-74,-236,-282,-275,-288,-27,-253,-54,-304,-244,-203,-232,-90,-87,-88,-235,-114,-69,-208,-207,-209,-59,-99,-231,-101,-150,232,-63,-37,-241,-225,-196,-229,-89,-221,-39,-239,-33,-75,-85,-237,-97,-28,-55,-233,-277,-263,-270,-256,-284,-298,-291,-111,-115,-110,-109,-70,-71,-60,-100,-246,-250,-247,-253,-151,-152,-102,-64,-65,-280,-266,-273,-259,-287,-301,-294,-107,-251,-252,-253,-245,-278,-264,-271,-257,-285,-299,-292,-248,-249,-253,-279,-265,-258,-286,-300,-293,-272,-40,-34,-96,-303,-302,-76,-79,-78,-77,-98,-30,-276,-262,-269,-255,-283,-297,-290,-56,-91,-92,-112,-113,-72,-73,-219,-61,-218,-155,-153,-154,-66,-67,-108,-41,-35,-213,-80,-83,-82,-81,-31,-57,-214,-156,-217,-216,-215,]),'IMAGINARY':([2,4,7,19,28,32,44,59,70,84,87,90,91,95,96,102,109,111,117,136,137,138,139,140,141,142,143,144,145,146,150,156,157,159,161,162,169,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,283,284,285,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,-46,-48,13,-49,-50,-45,-44,-47,-42,-43,-51,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,-52,13,-53,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,]),'IF':([2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,18,19,20,21,22,23,24,26,27,28,29,30,31,32,33,34,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,62,63,64,65,66,67,69,70,71,72,73,76,77,78,79,80,81,82,84,85,86,87,89,90,91,92,93,94,95,96,97,99,102,103,105,109,110,111,112,114,115,117,118,129,133,134,136,137,138,139,140,141,142,143,144,145,146,147,149,150,151,156,157,158,159,160,161,162,163,165,168,169,171,172,173,174,175,176,177,178,179,180,181,182,183,184,186,189,192,193,197,206,207,208,209,210,211,212,214,215,216,218,220,222,223,225,226,230,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,250,253,254,256,260,261,262,263,264,265,266,267,268,271,272,275,276,277,278,279,280,281,282,283,284,285,286,287,289,290,291,292,295,296,297,298,299,300,301,302,303,304,306,307,308,309,310,311,312,313,314,315,316,319,320,322,323,324,332,334,335,336,338,341,342,343,344,345,348,349,356,358,359,369,381,382,383,384,385,386,387,388,390,391,392,393,406,408,409,410,417,421,422,424,428,429,430,434,435,439,441,443,444,445,],[75,-296,75,-261,-205,75,-84,-268,-260,-234,-29,-200,-204,-68,-94,-58,75,-289,-93,-206,-86,-254,-36,-230,75,-62,-211,-95,75,-240,-224,-243,-228,-274,-281,-209,-267,-242,-227,75,-212,-199,-210,-223,-202,-222,-220,-196,-38,-197,-295,-238,-32,-201,75,-226,-198,-74,-236,-282,-275,-288,75,-27,-253,-54,-304,-244,-203,-232,-90,-87,-88,75,-235,-114,75,-69,75,75,-208,-207,-209,75,75,-59,-99,75,-231,-101,75,-63,75,-37,-241,-225,75,-196,-229,-89,-221,-46,-48,75,-49,-50,-45,-44,-47,-42,-43,-51,-39,-239,75,-33,75,75,-75,75,-85,75,75,-237,-97,-28,75,75,-55,75,75,-233,75,75,-277,-263,-270,-256,-284,-298,-291,75,-111,75,75,-115,75,-110,-109,75,75,-70,-71,-60,75,-100,75,75,-246,-250,-247,-253,75,-102,75,-64,75,75,-65,-280,-266,-273,-259,-287,-301,-294,-107,75,75,-251,-252,-253,-245,-278,-264,-271,-257,-285,-299,-292,-248,-249,-253,-279,-265,-258,-286,-300,-293,-272,-40,-52,75,-53,-34,75,75,-96,-303,-302,-76,75,75,75,75,-79,-78,-77,-98,75,75,-30,-276,-262,-269,-255,-283,-297,-290,-56,75,-91,-92,-112,75,75,75,75,-113,75,75,-72,-73,-219,-61,75,-146,-218,75,-66,-67,-108,-41,-35,75,-213,-80,-83,-82,-81,-31,-57,-214,75,75,-147,75,75,75,434,-148,75,439,75,-217,75,-149,75,-216,434,439,-215,]),'AND':([2,3,4,5,6,7,8,9,10,11,13,14,15,16,18,19,20,21,22,23,24,26,27,28,29,30,31,32,33,34,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,62,63,64,65,66,67,69,70,72,73,76,77,78,79,80,81,82,84,85,86,87,89,90,91,92,93,94,95,96,97,99,102,103,105,109,110,111,112,114,115,117,118,129,133,134,136,137,138,139,140,141,142,143,144,145,146,147,149,150,151,156,157,158,159,160,161,162,163,165,169,171,172,173,174,175,176,177,178,179,180,181,182,183,184,186,189,192,193,197,206,207,208,209,210,211,212,214,215,216,218,220,222,223,225,226,230,232,233,234,235,236,237,238,239,240,241,242,243,244,246,250,253,254,256,260,261,262,263,264,265,266,267,268,271,272,275,276,277,278,279,280,281,282,283,284,285,286,287,289,290,291,292,295,296,297,298,299,300,301,302,303,304,306,308,309,310,311,312,313,314,315,316,319,320,322,323,324,332,334,335,336,338,341,342,343,344,345,349,356,358,359,381,382,383,384,385,386,387,388,391,392,393,406,409,410,417,424,429,430,434,439,441,445,],[25,-296,25,-261,-205,25,-84,-268,-260,-234,-200,-204,-68,-94,-58,25,-289,-93,-206,-86,-254,-36,-230,25,-62,-211,-95,25,-240,-224,-243,-228,-274,-281,-209,-267,-242,-227,25,-212,-199,-210,-223,-202,-222,-220,-196,-38,-197,-295,-238,150,-201,25,-226,-198,-74,-236,-282,-275,-288,25,-253,-54,-304,-244,-203,-232,-90,-87,-88,25,-235,-114,25,-69,25,25,-208,-207,-209,25,25,-59,-99,25,-231,-101,25,-63,25,-37,-241,-225,25,-196,-229,-89,-221,-46,-48,25,-49,-50,-45,-44,-47,-42,-43,-51,-39,-239,25,287,25,25,-75,25,-85,25,25,-237,-97,25,25,-55,25,25,-233,25,25,-277,-263,-270,-256,-284,-298,-291,25,-111,25,25,-115,25,-110,-109,25,25,-70,-71,-60,25,-100,25,25,-246,-250,-247,-253,25,-102,25,-64,25,25,-65,-280,-266,-273,-259,-287,-301,-294,25,25,-251,-252,-253,-245,-278,-264,-271,-257,-285,-299,-292,-248,-249,-253,-279,-265,-258,-286,-300,-293,-272,-40,-52,25,-53,-34,25,25,-96,-303,-302,-76,25,25,25,25,-79,-78,-77,-98,25,25,-276,-262,-269,-255,-283,-297,-290,-56,25,-91,-92,-112,25,25,25,25,-113,25,25,-72,-73,-219,-61,25,-218,25,-66,-67,-41,-35,25,-213,-80,-83,-82,-81,-57,-214,25,25,25,25,25,25,25,-217,25,25,-216,-215,]),'FLNAKEDWORKSHEETREFERENCE':([2,4,7,19,28,32,44,59,70,84,87,90,91,95,96,102,104,109,111,117,128,132,136,137,138,139,140,141,142,143,144,145,146,150,156,157,159,161,162,169,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,283,284,285,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,-46,-48,76,-49,-50,-45,-44,-47,-42,-43,-51,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,-52,76,-53,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,76,]),'MOD_ITERP':([3,5,6,8,9,10,11,13,14,16,20,21,22,23,24,27,30,31,33,34,36,37,38,39,40,41,42,43,45,46,47,48,49,50,51,52,54,55,56,58,62,63,64,65,66,67,69,72,76,77,78,79,80,81,82,85,86,92,93,94,99,103,105,114,115,118,129,133,134,149,158,160,163,165,175,178,179,180,181,182,183,184,189,197,207,208,216,222,223,225,226,232,238,239,240,241,242,243,244,253,254,256,260,261,262,263,264,265,266,267,268,271,272,275,276,277,278,279,280,281,290,291,292,295,300,301,302,303,308,309,310,311,312,313,314,319,320,322,335,343,349,384,385,386,387,388,392,430,441,445,],[-296,-261,-205,-84,-268,-260,-234,-200,-204,-94,-289,-93,-206,-86,-254,-230,-211,-95,-240,-224,-243,-228,-274,-281,-209,-267,-242,-227,-212,-199,-210,-223,-202,-222,-220,-196,-197,-295,-238,-201,-226,-198,159,-236,-282,-275,-288,-253,-304,-244,-203,-232,-90,-87,-88,-235,-114,-208,-207,-209,-99,-231,-101,-241,-225,-196,-229,-89,-221,-239,297,-85,-237,-97,-233,-277,-263,-270,-256,-284,-298,-291,-111,-115,-110,-109,-100,-246,-250,-247,-253,-102,-280,-266,-273,-259,-287,-301,-294,-251,-252,-253,-245,-278,-264,-271,-257,-285,-299,-292,-248,-249,-253,-279,-265,-258,-286,-300,-293,-272,-96,-303,-302,-76,-79,-78,-77,-98,-276,-262,-269,-255,-283,-297,-290,-91,-92,-112,-113,-219,-218,-213,-80,-83,-82,-81,-214,-217,-216,-215,]),'NAME':([2,4,7,19,28,32,35,44,59,60,70,84,87,88,90,91,95,96,102,104,109,111,117,121,125,126,128,132,136,137,138,139,140,141,142,143,144,145,146,150,156,157,159,161,162,169,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,247,250,283,284,285,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,367,383,393,406,409,410,414,417,424,429,434,439,],[52,52,52,52,52,52,118,52,52,155,52,52,52,118,52,52,52,52,52,229,52,52,52,118,118,118,259,274,-46,-48,52,-49,-50,-45,-44,-47,-42,-43,-51,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,118,52,-52,52,-53,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,52,118,52,52,52,52,52,118,52,52,52,52,52,]),'UNEQUAL':([3,5,6,8,9,10,11,13,14,15,16,18,20,21,22,23,24,27,29,30,31,33,34,36,37,38,39,40,41,42,43,45,46,47,48,49,50,51,52,53,54,55,56,58,62,63,64,65,66,67,69,72,73,76,77,78,79,80,81,82,85,86,89,92,93,94,97,99,103,105,110,114,115,118,129,133,134,147,149,158,160,163,165,172,175,178,179,180,181,182,183,184,189,197,207,208,211,212,214,216,222,223,225,226,232,234,237,238,239,240,241,242,243,244,253,254,256,260,261,262,263,264,265,266,267,268,271,272,275,276,277,278,279,280,281,282,290,291,292,295,300,301,302,303,308,309,310,311,312,313,314,315,319,320,322,335,341,342,343,344,349,358,359,381,384,385,386,387,388,391,392,430,441,445,],[-296,-261,-205,-84,-268,-260,-234,-200,-204,-68,-94,-58,-289,-93,-206,-86,-254,-230,-62,-211,-95,-240,-224,-243,-228,-274,-281,-209,-267,-242,-227,-212,-199,-210,-223,-202,-222,-220,-196,137,-197,-295,-238,-201,-226,-198,-74,-236,-282,-275,-288,-253,-54,-304,-244,-203,-232,-90,-87,-88,-235,-114,-69,-208,-207,-209,-59,-99,-231,-101,-63,-241,-225,-196,-229,-89,-221,137,-239,-75,-85,-237,-97,-55,-233,-277,-263,-270,-256,-284,-298,-291,-111,-115,-110,-109,-70,-71,-60,-100,-246,-250,-247,-253,-102,-64,-65,-280,-266,-273,-259,-287,-301,-294,-251,-252,-253,-245,-278,-264,-271,-257,-285,-299,-292,-248,-249,-253,-279,-265,-258,-286,-300,-293,-272,-40,-96,-303,-302,-76,-79,-78,-77,-98,-276,-262,-269,-255,-283,-297,-290,-56,-91,-92,-112,-113,-72,-73,-219,-61,-218,-66,-67,-41,-213,-80,-83,-82,-81,-57,-214,-217,-216,-215,]),'COLONEQUALS':([3,5,6,8,9,10,11,12,13,14,15,16,18,20,21,22,23,24,26,27,29,30,31,33,34,36,37,38,39,40,41,42,43,45,46,47,48,49,50,51,52,53,54,55,56,57,58,62,63,64,65,66,67,69,71,72,73,76,77,78,79,80,81,82,85,86,89,92,93,94,97,99,103,105,110,112,114,115,118,119,120,129,133,134,147,149,151,158,160,163,165,168,172,175,178,179,180,181,182,183,184,189,194,197,207,208,211,212,214,216,222,223,225,226,232,234,237,238,239,240,241,242,243,244,245,253,254,256,260,261,262,263,264,265,266,267,268,271,272,275,276,277,278,279,280,281,282,286,290,291,292,295,300,301,302,303,307,308,309,310,311,312,313,314,315,319,320,322,328,335,341,342,343,344,349,358,359,362,368,369,381,382,384,385,386,387,388,390,391,392,396,430,441,445,],[-296,-261,-205,-84,-268,-260,-234,-29,-200,-204,-68,-94,-58,-289,-93,-206,-86,-254,-36,-230,-62,-211,-95,-240,-224,-243,-228,-274,-281,-209,-267,-242,-227,-212,-199,-210,-223,-202,-222,-220,-196,-38,-197,-295,-238,-32,-201,-226,-198,-74,-236,-282,-275,-288,-27,-253,-54,-304,-244,-203,-232,-90,-87,-88,-235,-114,-69,-208,-207,-209,-59,-99,-231,-101,-63,-37,-241,-225,-196,246,-19,-229,-89,-221,-39,-239,-33,-75,-85,-237,-97,-28,-55,-233,-277,-263,-270,-256,-284,-298,-291,-111,332,-115,-110,-109,-70,-71,-60,-100,-246,-250,-247,-253,-102,-64,-65,-280,-266,-273,-259,-287,-301,-294,-107,-251,-252,-253,-245,-278,-264,-271,-257,-285,-299,-292,-248,-249,-253,-279,-265,-258,-286,-300,-293,-272,-40,-34,-96,-303,-302,-76,-79,-78,-77,-98,-30,-276,-262,-269,-255,-283,-297,-290,-56,-91,-92,-112,332,-113,-72,-73,-219,-61,-218,-66,-67,246,-20,-108,-41,-35,-213,-80,-83,-82,-81,-31,-57,-214,332,-217,-216,-215,]),'DLONGSTRING':([2,4,6,7,16,19,22,28,30,32,40,44,45,47,59,70,84,87,90,91,92,93,94,95,96,102,109,111,117,136,137,138,139,140,141,142,143,144,145,146,150,156,157,159,161,162,169,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,283,284,285,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[30,30,-205,30,30,30,-206,30,-211,30,-209,30,-212,-210,30,30,30,30,30,30,-208,-207,-209,30,30,30,30,30,30,-46,-48,30,-49,-50,-45,-44,-47,-42,-43,-51,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,-52,30,-53,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,]),'DECINTEGER':([2,4,7,19,28,32,44,59,70,84,87,90,91,95,96,102,109,111,117,136,137,138,139,140,141,142,143,144,145,146,150,156,157,159,161,162,169,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,283,284,285,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,-46,-48,78,-49,-50,-45,-44,-47,-42,-43,-51,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,-52,78,-53,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,]),'LONGINVALIDREFERENCE':([2,4,7,19,28,32,44,59,70,84,87,90,91,95,96,102,104,109,111,117,128,136,137,138,139,140,141,142,143,144,145,146,150,156,157,159,161,162,169,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,283,284,285,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-46,-48,3,-49,-50,-45,-44,-47,-42,-43,-51,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-52,3,-53,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,]),'SLONGSTRING':([2,4,6,7,16,19,22,28,30,32,40,44,45,47,59,70,84,87,90,91,92,93,94,95,96,102,109,111,117,136,137,138,139,140,141,142,143,144,145,146,150,156,157,159,161,162,169,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,283,284,285,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[45,45,-205,45,45,45,-206,45,-211,45,-209,45,-212,-210,45,45,45,45,45,45,-208,-207,-209,45,45,45,45,45,45,-46,-48,45,-49,-50,-45,-44,-47,-42,-43,-51,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,-52,45,-53,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,]),'LEFTSHIFT':([3,5,6,8,9,10,11,13,14,15,16,20,21,22,23,24,27,29,30,31,33,34,36,37,38,39,40,41,42,43,45,46,47,48,49,50,51,52,54,55,56,58,62,63,64,65,66,67,69,72,76,77,78,79,80,81,82,85,86,89,92,93,94,99,103,105,110,114,115,118,129,133,134,149,158,160,163,165,175,178,179,180,181,182,183,184,189,197,207,208,211,212,216,222,223,225,226,232,234,237,238,239,240,241,242,243,244,253,254,256,260,261,262,263,264,265,266,267,268,271,272,275,276,277,278,279,280,281,290,291,292,295,300,301,302,303,308,309,310,311,312,313,314,319,320,322,335,341,342,343,349,358,359,384,385,386,387,388,392,430,441,445,],[-296,-261,-205,-84,-268,-260,-234,-200,-204,-68,-94,-289,-93,-206,-86,-254,-230,109,-211,-95,-240,-224,-243,-228,-274,-281,-209,-267,-242,-227,-212,-199,-210,-223,-202,-222,-220,-196,-197,-295,-238,-201,-226,-198,-74,-236,-282,-275,-288,-253,-304,-244,-203,-232,-90,-87,-88,-235,-114,-69,-208,-207,-209,-99,-231,-101,235,-241,-225,-196,-229,-89,-221,-239,-75,-85,-237,-97,-233,-277,-263,-270,-256,-284,-298,-291,-111,-115,-110,-109,-70,-71,-100,-246,-250,-247,-253,-102,-64,-65,-280,-266,-273,-259,-287,-301,-294,-251,-252,-253,-245,-278,-264,-271,-257,-285,-299,-292,-248,-249,-253,-279,-265,-258,-286,-300,-293,-272,-96,-303,-302,-76,-79,-78,-77,-98,-276,-262,-269,-255,-283,-297,-290,-91,-92,-112,-113,-72,-73,-219,-218,-66,-67,-213,-80,-83,-82,-81,-214,-217,-216,-215,]),'FLINVALIDREFERENCE':([2,4,7,19,28,32,44,59,70,83,84,87,90,91,95,96,102,104,109,111,113,117,128,131,135,136,137,138,139,140,141,142,143,144,145,146,150,156,157,159,161,162,169,170,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,283,284,285,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,350,351,352,353,354,356,371,372,373,374,375,383,393,406,409,410,417,424,429,434,439,],[55,55,55,55,55,55,55,55,55,183,55,55,55,55,55,55,55,55,55,55,243,55,55,266,279,-46,-48,55,-49,-50,-45,-44,-47,-42,-43,-51,55,55,55,55,55,55,55,313,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,-52,55,-53,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,55,266,313,243,183,279,55,266,313,243,183,279,55,55,55,55,55,55,55,55,55,55,]),'RIGHTPAREN':([3,5,6,8,9,10,11,12,13,14,15,16,18,20,21,22,23,24,26,27,29,30,31,33,34,36,37,38,39,40,41,42,43,45,46,47,48,49,50,51,52,53,54,55,56,57,58,62,63,64,65,66,67,69,70,71,72,73,76,77,78,79,80,81,82,84,85,86,89,92,93,94,97,99,103,105,110,112,114,115,118,120,129,133,134,147,149,151,158,160,163,164,165,166,167,168,172,175,178,179,180,181,182,183,184,185,187,188,189,190,191,193,194,195,196,197,207,208,211,212,213,214,216,217,220,221,222,223,225,226,232,234,237,238,239,240,241,242,243,244,245,248,249,253,254,256,260,261,262,263,264,265,266,267,268,271,272,275,276,277,278,279,280,281,282,286,290,291,292,293,294,295,300,301,302,303,305,307,308,309,310,311,312,313,314,315,317,319,320,321,322,324,325,326,327,328,329,330,331,335,341,342,343,344,345,348,349,358,359,366,367,368,369,381,382,384,385,386,387,388,390,391,392,395,397,398,399,400,401,402,403,408,414,415,418,419,427,428,429,430,437,438,440,441,442,444,445,447,],[-296,-261,-205,-84,-268,-260,-234,-29,-200,-204,-68,-94,-58,-289,-93,-206,-86,-254,-36,-230,-62,-211,-95,-240,-224,-243,-228,-274,-281,-209,-267,-242,-227,-212,-199,-210,-223,-202,-222,-220,-196,-38,-197,-295,-238,-32,-201,-226,-198,-74,-236,-282,-275,-288,165,-27,-253,-54,-304,-244,-203,-232,-90,-87,-88,189,-235,-114,-69,-208,-207,-209,-59,-99,-231,-101,-63,-37,-241,-225,-196,-19,-229,-89,-221,-39,-239,-33,-75,-85,-237,-105,-97,303,-142,-28,-55,-233,-277,-263,-270,-256,-284,-298,-291,-157,322,-158,-111,-164,-173,-168,-179,-159,-160,-115,-110,-109,-70,-71,343,-60,-100,-144,-143,349,-246,-250,-247,-253,-102,-64,-65,-280,-266,-273,-259,-287,-301,-294,-107,-21,368,-251,-252,-253,-245,-278,-264,-271,-257,-285,-299,-292,-248,-249,-253,-279,-265,-258,-286,-300,-293,-272,-40,-34,-96,-303,-302,384,-179,-76,-79,-78,-77,-98,-106,-30,-276,-262,-269,-255,-283,-297,-290,-56,392,-91,-92,-162,-112,-174,-161,-172,-171,-179,-170,-169,-180,-113,-72,-73,-219,-61,-145,-146,-218,-66,-67,-23,-22,-20,-108,-41,-35,-213,-80,-83,-82,-81,-31,-57,-214,-167,-166,-165,-178,-177,-176,-175,-181,-147,-24,-25,430,-163,-26,-190,441,-217,-188,-189,-191,-216,445,-192,-215,-193,]),'HEXINTEGER':([2,4,7,19,28,32,44,59,70,84,87,90,91,95,96,102,109,111,117,136,137,138,139,140,141,142,143,144,145,146,150,156,157,159,161,162,169,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,283,284,285,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,-46,-48,14,-49,-50,-45,-44,-47,-42,-43,-51,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,-52,14,-53,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,]),'COMMA':([3,5,6,8,9,10,11,12,13,14,15,16,18,20,21,22,23,24,26,27,29,30,31,33,34,36,37,38,39,40,41,42,43,45,46,47,48,49,50,51,52,53,54,55,56,57,58,62,63,64,65,66,67,69,71,72,73,76,77,78,79,80,81,82,84,85,86,89,92,93,94,95,97,99,101,102,103,105,106,110,112,114,115,118,119,120,129,133,134,147,149,151,152,158,160,163,165,167,168,172,175,178,179,180,181,182,183,184,189,190,191,193,194,197,198,201,202,203,204,205,206,207,208,211,212,214,216,217,222,223,225,226,231,232,234,237,238,239,240,241,242,243,244,245,248,252,253,254,256,260,261,262,263,264,265,266,267,268,271,272,275,276,277,278,279,280,281,282,286,288,290,291,292,294,295,300,301,302,303,307,308,309,310,311,312,313,314,315,318,319,320,321,322,324,328,331,333,334,335,336,337,339,340,341,342,343,344,347,348,349,355,357,358,359,360,362,366,368,369,381,382,384,385,386,387,388,390,391,392,403,404,405,407,408,411,412,415,418,420,422,423,427,428,430,435,436,437,438,440,441,444,445,447,],[-296,-261,-205,-84,-268,-260,-234,-29,-200,-204,-68,-94,-58,-289,-93,-206,-86,-254,-36,-230,-62,-211,-95,-240,-224,-243,-228,-274,-281,-209,-267,-242,-227,-212,-199,-210,-223,-202,-222,-220,-196,-38,-197,-295,-238,-32,-201,-226,-198,-74,-236,-282,-275,-288,-27,-253,-54,-304,-244,-203,-232,-90,-87,-88,193,-235,-114,-69,-208,-207,-209,193,-59,-99,220,193,-231,-101,230,-63,-37,-241,-225,-196,247,-19,-229,-89,-221,-39,-239,-33,289,-75,-85,-237,-97,220,-28,-55,-233,-277,-263,-270,-256,-284,-298,-291,-111,323,324,193,-179,-115,-126,-122,338,-123,-124,-125,-128,-110,-109,-70,-71,-60,-100,345,-246,-250,-247,-253,356,-102,-64,-65,-280,-266,-273,-259,-287,-301,-294,-107,367,370,-251,-252,-253,-245,-278,-264,-271,-257,-285,-299,-292,-248,-249,-253,-279,-265,-258,-286,-300,-293,-272,-40,-34,383,-96,-303,-302,-179,-76,-79,-78,-77,-98,-30,-276,-262,-269,-255,-283,-297,-290,-56,393,-91,-92,394,-112,193,-179,-180,-127,-134,-113,-129,406,-130,-133,-72,-73,-219,-61,410,-146,-218,-155,-154,-66,-67,413,247,414,-20,-108,-41,-35,-213,-80,-83,-82,-81,-31,-57,-214,-181,-135,-131,-120,-147,424,-156,-25,429,-121,289,-140,-26,-190,-217,383,-141,-188,-189,-191,-216,-192,-215,-193,]),'OR':([2,3,4,5,6,7,8,9,10,11,13,14,15,16,18,19,20,21,22,23,24,26,27,28,29,30,31,32,33,34,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,62,63,64,65,66,67,69,70,71,72,73,76,77,78,79,80,81,82,84,85,86,87,89,90,91,92,93,94,95,96,97,99,102,103,105,109,110,111,112,114,115,117,118,129,133,134,136,137,138,139,140,141,142,143,144,145,146,147,149,150,151,156,157,158,159,160,161,162,163,165,168,169,171,172,173,174,175,176,177,178,179,180,181,182,183,184,186,189,192,193,197,206,207,208,209,210,211,212,214,215,216,218,220,222,223,225,226,230,232,233,234,235,236,237,238,239,240,241,242,243,244,246,250,253,254,256,260,261,262,263,264,265,266,267,268,271,272,275,276,277,278,279,280,281,282,283,284,285,286,287,289,290,291,292,295,296,297,298,299,300,301,302,303,304,306,307,308,309,310,311,312,313,314,315,316,319,320,322,323,324,332,334,335,336,338,341,342,343,344,345,349,356,358,359,381,382,383,384,385,386,387,388,390,391,392,393,406,409,410,417,424,429,430,434,439,441,445,],[17,-296,17,-261,-205,17,-84,-268,-260,-234,-200,-204,-68,-94,-58,17,-289,-93,-206,-86,-254,-36,-230,17,-62,-211,-95,17,-240,-224,-243,-228,-274,-281,-209,-267,-242,-227,17,-212,-199,-210,-223,-202,-222,-220,-196,-38,-197,-295,-238,-32,-201,17,-226,-198,-74,-236,-282,-275,-288,17,169,-253,-54,-304,-244,-203,-232,-90,-87,-88,17,-235,-114,17,-69,17,17,-208,-207,-209,17,17,-59,-99,17,-231,-101,17,-63,17,-37,-241,-225,17,-196,-229,-89,-221,-46,-48,17,-49,-50,-45,-44,-47,-42,-43,-51,-39,-239,17,-33,17,17,-75,17,-85,17,17,-237,-97,306,17,17,-55,17,17,-233,17,17,-277,-263,-270,-256,-284,-298,-291,17,-111,17,17,-115,17,-110,-109,17,17,-70,-71,-60,17,-100,17,17,-246,-250,-247,-253,17,-102,17,-64,17,17,-65,-280,-266,-273,-259,-287,-301,-294,17,17,-251,-252,-253,-245,-278,-264,-271,-257,-285,-299,-292,-248,-249,-253,-279,-265,-258,-286,-300,-293,-272,-40,-52,17,-53,-34,17,17,-96,-303,-302,-76,17,17,17,17,-79,-78,-77,-98,17,17,-30,-276,-262,-269,-255,-283,-297,-290,-56,17,-91,-92,-112,17,17,17,17,-113,17,17,-72,-73,-219,-61,17,-218,17,-66,-67,-41,-35,17,-213,-80,-83,-82,-81,-31,-57,-214,17,17,17,17,17,17,17,-217,17,17,-216,-215,]),'LAMBDA':([2,19,28,59,70,84,87,95,102,117,156,173,174,186,192,193,206,220,230,233,246,250,289,323,324,332,334,336,338,345,356,383,393,406,409,417,429,434,439,],[35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,]),}\n\n_lr_action = { }\nfor _k, _v in _lr_action_items.items():\n   for _x,_y in zip(_v[0],_v[1]):\n      if not _x in _lr_action:  _lr_action[_x] = { }\n      _lr_action[_x][_k] = _y\ndel _lr_action_items\n\n_lr_goto_items = {'_partial_arglist_starting_with_argument':([84,95,102,193,324,],[185,185,185,326,399,]),'_short_slice':([87,338,406,],[198,198,198,]),'_name':([2,4,7,19,28,32,35,44,59,70,84,87,88,90,91,95,96,102,109,111,117,121,125,126,138,150,156,157,159,161,162,169,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,247,250,284,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,367,383,393,406,409,410,414,417,424,429,434,439,],[31,31,31,31,31,31,120,31,31,31,31,31,208,31,31,31,31,31,31,31,31,120,251,252,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,120,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,120,31,31,31,31,31,120,31,31,31,31,31,]),'_shortstring':([2,4,7,16,19,28,32,44,59,70,84,87,90,91,95,96,102,109,111,117,138,150,156,157,159,161,162,169,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,284,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[6,6,6,93,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,]),'factor':([2,4,7,19,28,32,44,59,70,84,87,90,91,95,96,102,109,111,117,138,150,156,157,159,161,162,169,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,284,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[8,81,82,8,8,8,133,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,319,320,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,]),'_a_op_terms':([15,],[89,]),'subscriptlist':([87,],[199,]),'_lower_bound':([87,338,406,],[200,200,200,]),'testlist':([19,70,],[98,164,]),'term':([2,19,28,32,59,70,84,87,90,91,95,96,102,109,111,117,138,150,156,169,171,173,174,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,284,287,289,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[15,15,15,15,15,15,15,15,211,212,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,341,342,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,]),'fl_row_reference':([2,4,7,19,28,32,44,59,70,84,87,90,91,95,96,102,109,111,117,138,150,156,157,159,161,162,169,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,284,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,]),'lambdef':([2,19,28,59,70,84,87,95,102,117,156,173,174,186,192,193,206,220,230,233,246,250,289,323,324,332,334,336,338,345,356,383,393,406,409,417,429,434,439,],[12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,]),'_simple_subscript':([87,338,406,],[201,201,201,]),'_comma_tests':([101,152,167,422,],[217,288,217,435,]),'stringliteral':([2,4,7,19,28,32,44,59,70,84,87,90,91,95,96,102,109,111,117,138,150,156,157,159,161,162,169,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,284,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,]),'list_iter':([421,443,],[431,446,]),'trailer':([11,27,33,34,37,51,56,65,79,85,103,114,115,129,134,149,163,175,],[86,86,86,86,86,86,86,86,86,197,197,197,197,197,197,197,197,197,]),'listmaker':([19,],[100,]),'arglist':([84,95,102,],[187,213,221,]),'shift_expr':([2,19,28,32,59,70,84,87,95,96,102,117,138,150,156,169,171,173,174,186,192,193,206,215,218,220,230,233,246,250,284,287,289,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[18,18,18,18,18,18,18,18,18,214,18,18,18,18,18,18,18,18,18,18,18,18,18,344,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,]),'_upper_bound':([206,336,],[339,405,]),'list_for':([101,421,443,],[219,433,433,]),'subscript':([87,338,406,],[202,407,420,]),'_number':([2,4,7,19,28,32,44,59,70,84,87,90,91,95,96,102,109,111,117,138,150,156,157,159,161,162,169,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,284,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,]),'_longstring':([2,4,7,16,19,28,32,44,59,70,84,87,90,91,95,96,102,109,111,117,138,150,156,157,159,161,162,169,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,284,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[22,22,22,92,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,]),'power':([2,4,7,19,28,32,44,59,70,84,87,90,91,95,96,102,109,111,117,138,150,156,157,159,161,162,169,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,284,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,]),'_partial_arglist_starting_with_keyword':([84,95,102,193,323,324,],[188,188,188,327,395,400,]),'_complex_subscript':([87,338,406,],[203,203,203,]),'_or_concat_exprs':([73,],[172,]),'sliceop':([198,],[333,]),'comparison':([2,19,28,32,59,70,84,87,95,102,117,150,156,169,173,174,186,192,193,206,220,230,233,246,250,287,289,306,323,324,332,334,336,338,345,356,383,393,406,409,417,429,434,439,],[26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,]),'fl_cell_reference':([2,4,7,19,28,32,44,59,70,84,87,90,91,95,96,102,104,109,111,117,128,132,138,150,156,157,159,161,162,169,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,284,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,222,27,27,27,253,268,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,]),'arith_expr':([2,19,28,32,59,70,84,87,95,96,102,109,111,117,138,150,156,169,171,173,174,186,192,193,206,215,218,220,230,233,235,236,246,250,284,287,289,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[29,29,29,29,29,29,29,29,29,29,29,234,237,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,358,359,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,]),'_partial_varargslist_starting_with_fpdef':([35,247,],[122,363,]),'_m_op_factors':([64,],[158,]),'fl_naked_worksheet_reference':([2,4,7,19,28,32,44,59,70,84,87,90,91,95,96,102,104,109,111,117,128,132,138,150,156,157,159,161,162,169,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,284,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,227,33,33,33,257,273,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,33,]),'if_function':([2,4,7,19,28,32,44,59,70,84,87,90,91,95,96,102,109,111,117,138,150,156,157,159,161,162,169,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,284,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,34,]),'fl_invalid_reference':([2,4,7,19,28,32,44,59,70,84,87,90,91,95,96,102,104,109,111,117,128,138,150,156,157,159,161,162,169,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,284,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,223,36,36,36,254,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,]),'fl_cell_range':([2,4,7,19,28,32,44,59,70,84,87,90,91,95,96,102,109,111,117,138,150,156,157,159,161,162,169,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,284,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,]),'_key_value_pair':([28,230,356,],[106,355,412,]),'gen_iter':([428,444,],[440,447,]),'_comma_subscripts':([202,],[337,]),'dictmaker':([28,],[107,]),'fl_deleted_reference':([2,4,7,19,28,32,44,59,70,84,87,90,91,95,96,102,104,109,111,117,132,138,150,156,157,159,161,162,169,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,284,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,225,42,42,42,271,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,]),'_comma_fpdefs':([248,],[366,]),'iserror_function':([2,4,7,19,28,32,44,59,70,84,87,90,91,95,96,102,109,111,117,138,150,156,157,159,161,162,169,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,284,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,43,]),'_imaginary':([2,4,7,19,28,32,44,59,70,84,87,90,91,95,96,102,109,111,117,138,150,156,157,159,161,162,169,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,284,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,]),'_partial_varargslist_starting_with_keyword':([35,247,],[116,361,]),'or_function':([2,4,7,19,28,32,44,59,70,84,87,90,91,95,96,102,109,111,117,138,150,156,157,159,161,162,169,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,284,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,]),'_comma_key_value_pairs':([106,],[231,]),'and_function':([2,4,7,19,28,32,44,59,70,84,87,90,91,95,96,102,109,111,117,138,150,156,157,159,161,162,169,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,284,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,]),'_keyword_argument':([84,95,102,193,323,324,],[190,190,190,190,190,190,]),'gen_if':([428,444,],[438,438,]),'_ampersand_shift_exprs':([18,],[97,]),'atom':([2,4,7,19,28,32,44,59,70,84,87,90,91,95,96,102,109,111,117,138,150,156,157,159,161,162,169,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,284,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,]),'_comp_operator_exprs':([53,],[147,]),'_shift_op_arith_exprs':([29,],[110,]),'exprlist':([218,304,],[346,389,]),'expr':([2,19,28,32,59,70,84,87,95,102,117,138,150,156,169,173,174,186,192,193,206,218,220,230,233,246,250,284,287,289,304,306,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[53,53,53,53,53,53,53,53,53,53,53,282,53,53,53,53,53,53,53,53,53,347,53,53,53,53,53,381,53,53,347,53,53,53,53,53,53,53,53,53,53,53,53,53,423,53,436,53,53,53,]),'_integer':([2,4,7,19,28,32,44,59,70,84,87,90,91,95,96,102,109,111,117,138,150,156,157,159,161,162,169,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,284,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,54,]),'fl_named_row_reference':([2,4,7,19,28,32,44,59,70,84,87,90,91,95,96,102,109,111,117,138,150,156,157,159,161,162,169,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,284,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,]),'comp_operator':([53,147,],[138,284,]),'argument':([84,95,102,156,173,174,193,324,393,429,],[191,191,191,293,317,318,191,191,418,442,]),'fplist':([121,],[249,]),'testlist_safe':([409,],[421,]),'not_test':([2,19,28,32,59,70,84,87,95,102,117,150,156,169,173,174,186,192,193,206,220,230,233,246,250,287,289,306,323,324,332,334,336,338,345,356,383,393,406,409,417,429,434,439,],[57,57,57,112,57,57,57,57,57,57,57,286,57,57,57,57,57,57,57,57,57,57,57,57,57,382,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,]),'_partial_varargslist_starting_with_star':([35,247,413,],[127,365,425,]),'iserr_function':([2,4,7,19,28,32,44,59,70,84,87,90,91,95,96,102,109,111,117,138,150,156,157,159,161,162,169,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,284,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,]),'_float':([2,4,7,19,28,32,44,59,70,84,87,90,91,95,96,102,109,111,117,138,150,156,157,159,161,162,169,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,284,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,]),'_root':([0,],[1,]),'percent':([2,19,28,32,59,70,84,87,90,91,95,96,102,109,111,117,138,150,156,157,159,161,162,169,171,173,174,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,284,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,295,300,301,302,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,385,386,387,388,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,]),'fl_column_reference':([2,4,7,19,28,32,44,59,70,84,87,90,91,95,96,102,109,111,117,138,150,156,157,159,161,162,169,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,284,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,79,]),'fl_named_column_reference':([2,4,7,19,28,32,44,59,70,84,87,90,91,95,96,102,109,111,117,138,150,156,157,159,161,162,169,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,284,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,]),'testlist_gexp':([70,],[166,]),'list_if':([421,443,],[432,432,]),'test':([2,19,28,59,70,84,87,95,102,117,156,173,174,186,192,193,206,220,230,233,246,250,289,323,324,332,334,336,338,345,356,383,393,406,409,417,429,434,439,],[68,101,108,152,167,194,205,194,194,245,294,294,294,321,325,328,340,348,108,357,360,369,348,396,328,403,404,340,205,408,108,408,294,205,422,428,294,443,444,]),'gen_for':([167,194,294,328,428,444,],[305,331,331,331,437,437,]),'_or_and_tests':([71,],[168,]),'and_test':([2,19,28,59,70,84,87,95,102,117,156,169,173,174,186,192,193,206,220,230,233,246,250,289,306,323,324,332,334,336,338,345,356,383,393,406,409,417,429,434,439,],[71,71,71,71,71,71,71,71,71,71,71,307,71,71,71,71,71,71,71,71,71,71,71,71,390,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,71,]),'_partial_arglist_starting_with_star':([84,95,102,193,323,324,],[195,195,195,329,397,401,]),'concat_expr':([2,19,28,32,59,70,84,87,95,102,117,138,150,156,169,171,173,174,186,192,193,206,218,220,230,233,246,250,284,287,289,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,315,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,391,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,]),'_partial_varargslist_starting_with_doublestar':([35,247,370,413,],[124,364,416,426,]),'_partial_arglist_starting_with_doublestar':([84,95,102,193,323,324,394,],[196,196,196,330,398,402,419,]),'_and_not_tests':([57,],[151,]),'fl_dde_call':([2,4,7,19,28,32,44,59,70,84,87,90,91,95,96,102,109,111,117,138,150,156,157,159,161,162,169,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,284,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,]),'_trailers':([11,27,33,34,37,51,56,65,79,],[85,103,114,115,129,134,149,163,175,]),'varargslist':([35,],[123,]),'testlist1':([59,],[153,]),'fl_reference':([2,4,7,19,28,32,44,59,70,84,87,90,91,95,96,102,109,111,117,138,150,156,157,159,161,162,169,171,173,174,176,177,186,192,193,206,209,210,215,218,220,230,233,235,236,246,250,284,287,289,296,297,298,299,304,306,316,323,324,332,334,336,338,345,356,383,393,406,409,410,417,424,429,434,439,],[80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,]),'_comma_exprs':([347,],[411,]),'fpdef':([35,121,247,367,414,],[119,248,362,415,427,]),}\n\n_lr_goto = { }\nfor _k, _v in _lr_goto_items.items():\n   for _x,_y in zip(_v[0],_v[1]):\n       if not _x in _lr_goto: _lr_goto[_x] = { }\n       _lr_goto[_x][_k] = _y\ndel _lr_goto_items\n_lr_productions = [\n  (\"S' -> _root\",\"S'\",1,None,None,None),\n  ('_root -> EQUALS test','_root',2,'p__root','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',73),\n  ('varargslist -> _partial_varargslist_starting_with_fpdef','varargslist',1,'p_varargslist','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',84),\n  ('varargslist -> _partial_varargslist_starting_with_keyword','varargslist',1,'p_varargslist','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',85),\n  ('varargslist -> _partial_varargslist_starting_with_star','varargslist',1,'p_varargslist','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',86),\n  ('varargslist -> _partial_varargslist_starting_with_doublestar','varargslist',1,'p_varargslist','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',87),\n  ('_partial_varargslist_starting_with_doublestar -> DOUBLESTAR _name','_partial_varargslist_starting_with_doublestar',2,'p__partial_varargslist_starting_with_doublestar','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',92),\n  ('_partial_varargslist_starting_with_star -> STAR _name','_partial_varargslist_starting_with_star',2,'p__partial_varargslist_starting_with_star','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',97),\n  ('_partial_varargslist_starting_with_star -> STAR _name COMMA _partial_varargslist_starting_with_doublestar','_partial_varargslist_starting_with_star',4,'p__partial_varargslist_starting_with_star','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',98),\n  ('_partial_varargslist_starting_with_keyword -> fpdef COLONEQUALS test','_partial_varargslist_starting_with_keyword',3,'p__partial_varargslist_starting_with_keyword','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',106),\n  ('_partial_varargslist_starting_with_keyword -> fpdef COLONEQUALS test COMMA','_partial_varargslist_starting_with_keyword',4,'p__partial_varargslist_starting_with_keyword','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',107),\n  ('_partial_varargslist_starting_with_keyword -> fpdef COLONEQUALS test COMMA _partial_varargslist_starting_with_doublestar','_partial_varargslist_starting_with_keyword',5,'p__partial_varargslist_starting_with_keyword','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',108),\n  ('_partial_varargslist_starting_with_keyword -> fpdef COLONEQUALS test COMMA _partial_varargslist_starting_with_star','_partial_varargslist_starting_with_keyword',5,'p__partial_varargslist_starting_with_keyword','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',109),\n  ('_partial_varargslist_starting_with_fpdef -> fpdef','_partial_varargslist_starting_with_fpdef',1,'p__partial_varargslist_starting_with_fpdef','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',117),\n  ('_partial_varargslist_starting_with_fpdef -> fpdef COMMA','_partial_varargslist_starting_with_fpdef',2,'p__partial_varargslist_starting_with_fpdef','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',118),\n  ('_partial_varargslist_starting_with_fpdef -> fpdef COMMA _partial_varargslist_starting_with_doublestar','_partial_varargslist_starting_with_fpdef',3,'p__partial_varargslist_starting_with_fpdef','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',119),\n  ('_partial_varargslist_starting_with_fpdef -> fpdef COMMA _partial_varargslist_starting_with_star','_partial_varargslist_starting_with_fpdef',3,'p__partial_varargslist_starting_with_fpdef','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',120),\n  ('_partial_varargslist_starting_with_fpdef -> fpdef COMMA _partial_varargslist_starting_with_keyword','_partial_varargslist_starting_with_fpdef',3,'p__partial_varargslist_starting_with_fpdef','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',121),\n  ('_partial_varargslist_starting_with_fpdef -> fpdef COMMA _partial_varargslist_starting_with_fpdef','_partial_varargslist_starting_with_fpdef',3,'p__partial_varargslist_starting_with_fpdef','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',122),\n  ('fpdef -> _name','fpdef',1,'p_fpdef','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',130),\n  ('fpdef -> LEFTPAREN fplist RIGHTPAREN','fpdef',3,'p_fpdef','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',131),\n  ('fplist -> fpdef','fplist',1,'p_fplist','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',137),\n  ('fplist -> fpdef COMMA','fplist',2,'p_fplist','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',138),\n  ('fplist -> fpdef _comma_fpdefs','fplist',2,'p_fplist','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',139),\n  ('fplist -> fpdef _comma_fpdefs COMMA','fplist',3,'p_fplist','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',140),\n  ('_comma_fpdefs -> COMMA fpdef','_comma_fpdefs',2,'p__comma_fpdefs','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',153),\n  ('_comma_fpdefs -> _comma_fpdefs COMMA fpdef','_comma_fpdefs',3,'p__comma_fpdefs','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',154),\n  ('test -> and_test','test',1,'p_test','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',162),\n  ('test -> and_test _or_and_tests','test',2,'p_test','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',163),\n  ('test -> lambdef','test',1,'p_test','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',164),\n  ('_or_and_tests -> OR and_test','_or_and_tests',2,'p__or_and_tests','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',172),\n  ('_or_and_tests -> _or_and_tests OR and_test','_or_and_tests',3,'p__or_and_tests','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',173),\n  ('and_test -> not_test','and_test',1,'p_and_test','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',181),\n  ('and_test -> not_test _and_not_tests','and_test',2,'p_and_test','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',182),\n  ('_and_not_tests -> AND not_test','_and_not_tests',2,'p__and_not_tests','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',190),\n  ('_and_not_tests -> _and_not_tests AND not_test','_and_not_tests',3,'p__and_not_tests','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',191),\n  ('not_test -> comparison','not_test',1,'p_not_test','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',199),\n  ('not_test -> NOT not_test','not_test',2,'p_not_test','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',200),\n  ('comparison -> expr','comparison',1,'p_comparison','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',208),\n  ('comparison -> expr _comp_operator_exprs','comparison',2,'p_comparison','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',209),\n  ('_comp_operator_exprs -> comp_operator expr','_comp_operator_exprs',2,'p__comp_operator_exprs','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',217),\n  ('_comp_operator_exprs -> _comp_operator_exprs comp_operator expr','_comp_operator_exprs',3,'p__comp_operator_exprs','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',218),\n  ('comp_operator -> LESSTHAN','comp_operator',1,'p_comp_operator','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',226),\n  ('comp_operator -> GREATERTHAN','comp_operator',1,'p_comp_operator','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',227),\n  ('comp_operator -> EQUALS','comp_operator',1,'p_comp_operator','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',228),\n  ('comp_operator -> EQUALTO','comp_operator',1,'p_comp_operator','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',229),\n  ('comp_operator -> GREATEREQUAL','comp_operator',1,'p_comp_operator','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',230),\n  ('comp_operator -> LESSEQUAL','comp_operator',1,'p_comp_operator','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',231),\n  ('comp_operator -> UNEQUAL','comp_operator',1,'p_comp_operator','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',232),\n  ('comp_operator -> OBSOLETEUNEQUAL','comp_operator',1,'p_comp_operator','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',233),\n  ('comp_operator -> IS','comp_operator',1,'p_comp_operator','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',234),\n  ('comp_operator -> IN','comp_operator',1,'p_comp_operator','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',235),\n  ('comp_operator -> IS NOT','comp_operator',2,'p_comp_operator','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',236),\n  ('comp_operator -> NOT IN','comp_operator',2,'p_comp_operator','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',237),\n  ('expr -> concat_expr','expr',1,'p_expr','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',245),\n  ('expr -> concat_expr _or_concat_exprs','expr',2,'p_expr','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',246),\n  ('_or_concat_exprs -> VERTICALBAR concat_expr','_or_concat_exprs',2,'p__or_concat_exprs','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',254),\n  ('_or_concat_exprs -> _or_concat_exprs VERTICALBAR concat_expr','_or_concat_exprs',3,'p__or_concat_exprs','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',255),\n  ('concat_expr -> shift_expr','concat_expr',1,'p_concat_expr','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',263),\n  ('concat_expr -> shift_expr _ampersand_shift_exprs','concat_expr',2,'p_concat_expr','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',264),\n  ('_ampersand_shift_exprs -> AMPERSAND shift_expr','_ampersand_shift_exprs',2,'p__ampersand_shift_exprs','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',272),\n  ('_ampersand_shift_exprs -> _ampersand_shift_exprs AMPERSAND shift_expr','_ampersand_shift_exprs',3,'p__ampersand_shift_exprs','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',273),\n  ('shift_expr -> arith_expr','shift_expr',1,'p_shift_expr','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',281),\n  ('shift_expr -> arith_expr _shift_op_arith_exprs','shift_expr',2,'p_shift_expr','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',282),\n  ('_shift_op_arith_exprs -> LEFTSHIFT arith_expr','_shift_op_arith_exprs',2,'p__shift_op_arith_exprs','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',290),\n  ('_shift_op_arith_exprs -> RIGHTSHIFT arith_expr','_shift_op_arith_exprs',2,'p__shift_op_arith_exprs','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',291),\n  ('_shift_op_arith_exprs -> _shift_op_arith_exprs LEFTSHIFT arith_expr','_shift_op_arith_exprs',3,'p__shift_op_arith_exprs','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',292),\n  ('_shift_op_arith_exprs -> _shift_op_arith_exprs RIGHTSHIFT arith_expr','_shift_op_arith_exprs',3,'p__shift_op_arith_exprs','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',293),\n  ('arith_expr -> term','arith_expr',1,'p_arith_expr','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',301),\n  ('arith_expr -> term _a_op_terms','arith_expr',2,'p_arith_expr','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',302),\n  ('_a_op_terms -> PLUS term','_a_op_terms',2,'p__a_op_terms','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',310),\n  ('_a_op_terms -> MINUS term','_a_op_terms',2,'p__a_op_terms','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',311),\n  ('_a_op_terms -> _a_op_terms PLUS term','_a_op_terms',3,'p__a_op_terms','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',312),\n  ('_a_op_terms -> _a_op_terms MINUS term','_a_op_terms',3,'p__a_op_terms','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',313),\n  ('term -> percent','term',1,'p_term','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',321),\n  ('term -> percent _m_op_factors','term',2,'p_term','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',322),\n  ('_m_op_factors -> STAR percent','_m_op_factors',2,'p__m_op_factors','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',330),\n  ('_m_op_factors -> SLASH percent','_m_op_factors',2,'p__m_op_factors','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',331),\n  ('_m_op_factors -> DOUBLESLASH percent','_m_op_factors',2,'p__m_op_factors','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',332),\n  ('_m_op_factors -> MOD_ITERP percent','_m_op_factors',2,'p__m_op_factors','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',333),\n  ('_m_op_factors -> _m_op_factors STAR percent','_m_op_factors',3,'p__m_op_factors','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',334),\n  ('_m_op_factors -> _m_op_factors SLASH percent','_m_op_factors',3,'p__m_op_factors','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',335),\n  ('_m_op_factors -> _m_op_factors DOUBLESLASH percent','_m_op_factors',3,'p__m_op_factors','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',336),\n  ('_m_op_factors -> _m_op_factors MOD_ITERP percent','_m_op_factors',3,'p__m_op_factors','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',337),\n  ('percent -> factor','percent',1,'p_percent','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',344),\n  ('percent -> percent PERCENT','percent',2,'p_percent','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',345),\n  ('factor -> power','factor',1,'p_factor','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',352),\n  ('factor -> MINUS factor','factor',2,'p_factor','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',353),\n  ('factor -> PLUS factor','factor',2,'p_factor','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',354),\n  ('factor -> TILDE factor','factor',2,'p_factor','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',355),\n  ('power -> fl_reference','power',1,'p_power','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',360),\n  ('power -> fl_reference DOUBLESTAR factor','power',3,'p_power','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',361),\n  ('power -> fl_reference CIRCUMFLEX factor','power',3,'p_power','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',362),\n  ('atom -> _number','atom',1,'p_atom','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',370),\n  ('atom -> stringliteral','atom',1,'p_atom','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',371),\n  ('atom -> _name','atom',1,'p_atom','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',372),\n  ('atom -> BACKWARDQUOTE testlist1 BACKWARDQUOTE','atom',3,'p_atom','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',373),\n  ('atom -> LEFTPAREN RIGHTPAREN','atom',2,'p_atom','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',374),\n  ('atom -> LEFTPAREN testlist_gexp RIGHTPAREN','atom',3,'p_atom','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',375),\n  ('atom -> LEFTBRACKET RIGHTBRACKET','atom',2,'p_atom','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',376),\n  ('atom -> LEFTBRACKET listmaker RIGHTBRACKET','atom',3,'p_atom','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',377),\n  ('atom -> LEFTBRACE RIGHTBRACE','atom',2,'p_atom','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',378),\n  ('atom -> LEFTBRACE dictmaker RIGHTBRACE','atom',3,'p_atom','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',379),\n  ('listmaker -> testlist','listmaker',1,'p_listmaker','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',384),\n  ('listmaker -> test list_for','listmaker',2,'p_listmaker','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',385),\n  ('testlist_gexp -> testlist','testlist_gexp',1,'p_testlist_gexp','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',393),\n  ('testlist_gexp -> test gen_for','testlist_gexp',2,'p_testlist_gexp','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',394),\n  ('lambdef -> LAMBDA LITTLEARROW test','lambdef',3,'p_lambdef','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',402),\n  ('lambdef -> LAMBDA varargslist LITTLEARROW test','lambdef',4,'p_lambdef','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',403),\n  ('trailer -> DOT _name','trailer',2,'p_trailer','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',408),\n  ('trailer -> DOT FLCELLREFLIKENAME','trailer',2,'p_trailer','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',409),\n  ('trailer -> LEFTPAREN RIGHTPAREN','trailer',2,'p_trailer','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',410),\n  ('trailer -> LEFTPAREN arglist RIGHTPAREN','trailer',3,'p_trailer','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',411),\n  ('trailer -> LEFTBRACKET subscriptlist RIGHTBRACKET','trailer',3,'p_trailer','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',412),\n  ('_trailers -> trailer','_trailers',1,'p__trailers','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',425),\n  ('_trailers -> _trailers trailer','_trailers',2,'p__trailers','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',426),\n  ('subscriptlist -> subscript','subscriptlist',1,'p_subscriptlist','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',434),\n  ('subscriptlist -> subscript COMMA','subscriptlist',2,'p_subscriptlist','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',435),\n  ('subscriptlist -> subscript _comma_subscripts','subscriptlist',2,'p_subscriptlist','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',436),\n  ('subscriptlist -> subscript _comma_subscripts COMMA','subscriptlist',3,'p_subscriptlist','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',437),\n  ('_comma_subscripts -> COMMA subscript','_comma_subscripts',2,'p__comma_subscripts','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',450),\n  ('_comma_subscripts -> _comma_subscripts COMMA subscript','_comma_subscripts',3,'p__comma_subscripts','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',451),\n  ('subscript -> _simple_subscript','subscript',1,'p_subscript','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',459),\n  ('subscript -> _complex_subscript','subscript',1,'p_subscript','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',460),\n  ('_simple_subscript -> ELLIPSIS','_simple_subscript',1,'p__simple_subscript','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',465),\n  ('_simple_subscript -> test','_simple_subscript',1,'p__simple_subscript','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',466),\n  ('_complex_subscript -> _short_slice','_complex_subscript',1,'p__complex_subscript','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',471),\n  ('_complex_subscript -> _short_slice sliceop','_complex_subscript',2,'p__complex_subscript','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',472),\n  ('_short_slice -> LITTLEARROW','_short_slice',1,'p__short_slice','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',480),\n  ('_short_slice -> _lower_bound LITTLEARROW','_short_slice',2,'p__short_slice','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',481),\n  ('_short_slice -> LITTLEARROW _upper_bound','_short_slice',2,'p__short_slice','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',482),\n  ('_short_slice -> _lower_bound LITTLEARROW _upper_bound','_short_slice',3,'p__short_slice','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',483),\n  ('_lower_bound -> test','_lower_bound',1,'p__lower_bound','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',488),\n  ('_upper_bound -> test','_upper_bound',1,'p__upper_bound','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',493),\n  ('sliceop -> LITTLEARROW','sliceop',1,'p_sliceop','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',498),\n  ('sliceop -> LITTLEARROW test','sliceop',2,'p_sliceop','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',499),\n  ('exprlist -> expr','exprlist',1,'p_exprlist','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',504),\n  ('exprlist -> expr COMMA','exprlist',2,'p_exprlist','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',505),\n  ('exprlist -> expr _comma_exprs','exprlist',2,'p_exprlist','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',506),\n  ('exprlist -> expr _comma_exprs COMMA','exprlist',3,'p_exprlist','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',507),\n  ('_comma_exprs -> COMMA expr','_comma_exprs',2,'p__comma_exprs','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',520),\n  ('_comma_exprs -> _comma_exprs COMMA expr','_comma_exprs',3,'p__comma_exprs','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',521),\n  ('testlist -> test','testlist',1,'p_testlist','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',529),\n  ('testlist -> test COMMA','testlist',2,'p_testlist','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',530),\n  ('testlist -> test _comma_tests','testlist',2,'p_testlist','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',531),\n  ('testlist -> test _comma_tests COMMA','testlist',3,'p_testlist','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',532),\n  ('_comma_tests -> COMMA test','_comma_tests',2,'p__comma_tests','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',545),\n  ('_comma_tests -> _comma_tests COMMA test','_comma_tests',3,'p__comma_tests','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',546),\n  ('testlist_safe -> test','testlist_safe',1,'p_testlist_safe','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',554),\n  ('testlist_safe -> test _comma_tests','testlist_safe',2,'p_testlist_safe','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',555),\n  ('dictmaker -> _key_value_pair','dictmaker',1,'p_dictmaker','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',566),\n  ('dictmaker -> _key_value_pair COMMA','dictmaker',2,'p_dictmaker','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',567),\n  ('dictmaker -> _key_value_pair _comma_key_value_pairs','dictmaker',2,'p_dictmaker','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',568),\n  ('dictmaker -> _key_value_pair _comma_key_value_pairs COMMA','dictmaker',3,'p_dictmaker','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',569),\n  ('_key_value_pair -> test LITTLEARROW test','_key_value_pair',3,'p__key_value_pair','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',582),\n  ('_comma_key_value_pairs -> COMMA _key_value_pair','_comma_key_value_pairs',2,'p__comma_key_value_pairs','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',587),\n  ('_comma_key_value_pairs -> _comma_key_value_pairs COMMA _key_value_pair','_comma_key_value_pairs',3,'p__comma_key_value_pairs','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',588),\n  ('arglist -> _partial_arglist_starting_with_argument','arglist',1,'p_arglist','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',596),\n  ('arglist -> _partial_arglist_starting_with_keyword','arglist',1,'p_arglist','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',597),\n  ('arglist -> _partial_arglist_starting_with_star','arglist',1,'p_arglist','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',598),\n  ('arglist -> _partial_arglist_starting_with_doublestar','arglist',1,'p_arglist','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',599),\n  ('_partial_arglist_starting_with_doublestar -> DOUBLESTAR test','_partial_arglist_starting_with_doublestar',2,'p__partial_arglist_starting_with_doublestar','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',604),\n  ('_partial_arglist_starting_with_star -> STAR test','_partial_arglist_starting_with_star',2,'p__partial_arglist_starting_with_star','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',609),\n  ('_partial_arglist_starting_with_star -> STAR test COMMA _partial_arglist_starting_with_doublestar','_partial_arglist_starting_with_star',4,'p__partial_arglist_starting_with_star','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',610),\n  ('_partial_arglist_starting_with_keyword -> _keyword_argument','_partial_arglist_starting_with_keyword',1,'p__partial_arglist_starting_with_keyword','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',618),\n  ('_partial_arglist_starting_with_keyword -> _keyword_argument COMMA _partial_arglist_starting_with_doublestar','_partial_arglist_starting_with_keyword',3,'p__partial_arglist_starting_with_keyword','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',619),\n  ('_partial_arglist_starting_with_keyword -> _keyword_argument COMMA _partial_arglist_starting_with_star','_partial_arglist_starting_with_keyword',3,'p__partial_arglist_starting_with_keyword','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',620),\n  ('_partial_arglist_starting_with_keyword -> _keyword_argument COMMA _partial_arglist_starting_with_keyword','_partial_arglist_starting_with_keyword',3,'p__partial_arglist_starting_with_keyword','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',621),\n  ('_partial_arglist_starting_with_argument -> COMMA','_partial_arglist_starting_with_argument',1,'p__partial_arglist_starting_with_argument','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',629),\n  ('_partial_arglist_starting_with_argument -> COMMA _partial_arglist_starting_with_doublestar','_partial_arglist_starting_with_argument',2,'p__partial_arglist_starting_with_argument','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',630),\n  ('_partial_arglist_starting_with_argument -> COMMA _partial_arglist_starting_with_star','_partial_arglist_starting_with_argument',2,'p__partial_arglist_starting_with_argument','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',631),\n  ('_partial_arglist_starting_with_argument -> COMMA _partial_arglist_starting_with_keyword','_partial_arglist_starting_with_argument',2,'p__partial_arglist_starting_with_argument','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',632),\n  ('_partial_arglist_starting_with_argument -> COMMA _partial_arglist_starting_with_argument','_partial_arglist_starting_with_argument',2,'p__partial_arglist_starting_with_argument','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',633),\n  ('_partial_arglist_starting_with_argument -> argument','_partial_arglist_starting_with_argument',1,'p__partial_arglist_starting_with_argument','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',634),\n  ('_partial_arglist_starting_with_argument -> argument COMMA','_partial_arglist_starting_with_argument',2,'p__partial_arglist_starting_with_argument','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',635),\n  ('_partial_arglist_starting_with_argument -> argument COMMA _partial_arglist_starting_with_doublestar','_partial_arglist_starting_with_argument',3,'p__partial_arglist_starting_with_argument','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',636),\n  ('_partial_arglist_starting_with_argument -> argument COMMA _partial_arglist_starting_with_star','_partial_arglist_starting_with_argument',3,'p__partial_arglist_starting_with_argument','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',637),\n  ('_partial_arglist_starting_with_argument -> argument COMMA _partial_arglist_starting_with_keyword','_partial_arglist_starting_with_argument',3,'p__partial_arglist_starting_with_argument','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',638),\n  ('_partial_arglist_starting_with_argument -> argument COMMA _partial_arglist_starting_with_argument','_partial_arglist_starting_with_argument',3,'p__partial_arglist_starting_with_argument','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',639),\n  ('argument -> test','argument',1,'p_argument','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',652),\n  ('argument -> test gen_for','argument',2,'p_argument','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',653),\n  ('_keyword_argument -> test COLONEQUALS test','_keyword_argument',3,'p__keyword_argument','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',658),\n  ('list_iter -> list_for','list_iter',1,'p_list_iter','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',663),\n  ('list_iter -> list_if','list_iter',1,'p_list_iter','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',664),\n  ('list_for -> FOR exprlist IN testlist_safe','list_for',4,'p_list_for','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',669),\n  ('list_for -> FOR exprlist IN testlist_safe list_iter','list_for',5,'p_list_for','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',670),\n  ('list_if -> IF test','list_if',2,'p_list_if','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',675),\n  ('list_if -> IF test list_iter','list_if',3,'p_list_if','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',676),\n  ('gen_iter -> gen_for','gen_iter',1,'p_gen_iter','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',681),\n  ('gen_iter -> gen_if','gen_iter',1,'p_gen_iter','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',682),\n  ('gen_for -> FOR exprlist IN test','gen_for',4,'p_gen_for','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',687),\n  ('gen_for -> FOR exprlist IN test gen_iter','gen_for',5,'p_gen_for','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',688),\n  ('gen_if -> IF test','gen_if',2,'p_gen_if','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',693),\n  ('gen_if -> IF test gen_iter','gen_if',3,'p_gen_if','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',694),\n  ('testlist1 -> test','testlist1',1,'p_testlist1','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',699),\n  ('testlist1 -> test _comma_tests','testlist1',2,'p_testlist1','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',700),\n  ('_name -> NAME','_name',1,'p__name','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',708),\n  ('_number -> _integer','_number',1,'p__number','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',713),\n  ('_number -> _float','_number',1,'p__number','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',714),\n  ('_number -> _imaginary','_number',1,'p__number','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',715),\n  ('_imaginary -> IMAGINARY','_imaginary',1,'p__imaginary','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',720),\n  ('_float -> FLOATNUMBER','_float',1,'p__float','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',725),\n  ('_integer -> OCTINTEGER','_integer',1,'p__integer','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',730),\n  ('_integer -> DECINTEGER','_integer',1,'p__integer','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',731),\n  ('_integer -> HEXINTEGER','_integer',1,'p__integer','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',732),\n  ('stringliteral -> _shortstring','stringliteral',1,'p_stringliteral','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',737),\n  ('stringliteral -> _longstring','stringliteral',1,'p_stringliteral','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',738),\n  ('stringliteral -> stringliteral _shortstring','stringliteral',2,'p_stringliteral','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',739),\n  ('stringliteral -> stringliteral _longstring','stringliteral',2,'p_stringliteral','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',740),\n  ('_shortstring -> SSHORTSTRING','_shortstring',1,'p__shortstring','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',748),\n  ('_shortstring -> DSHORTSTRING','_shortstring',1,'p__shortstring','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',749),\n  ('_longstring -> DLONGSTRING','_longstring',1,'p__longstring','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',754),\n  ('_longstring -> SLONGSTRING','_longstring',1,'p__longstring','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',755),\n  ('iserror_function -> ISERROR LEFTPAREN argument RIGHTPAREN','iserror_function',4,'p_iserror_function','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',765),\n  ('iserr_function -> ISERR LEFTPAREN argument RIGHTPAREN','iserr_function',4,'p_iserr_function','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',770),\n  ('if_function -> IF LEFTPAREN argument COMMA argument COMMA argument RIGHTPAREN','if_function',8,'p_if_function','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',775),\n  ('if_function -> IF LEFTPAREN argument COMMA argument COMMA RIGHTPAREN','if_function',7,'p_if_function','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',776),\n  ('if_function -> IF LEFTPAREN argument COMMA argument RIGHTPAREN','if_function',6,'p_if_function','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',777),\n  ('and_function -> AND LEFTPAREN arglist RIGHTPAREN','and_function',4,'p_and_function','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',782),\n  ('or_function -> OR LEFTPAREN arglist RIGHTPAREN','or_function',4,'p_or_function','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',787),\n  ('fl_reference -> atom','fl_reference',1,'p_fl_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',792),\n  ('fl_reference -> atom _trailers','fl_reference',2,'p_fl_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',793),\n  ('fl_reference -> and_function','fl_reference',1,'p_fl_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',794),\n  ('fl_reference -> or_function','fl_reference',1,'p_fl_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',795),\n  ('fl_reference -> if_function','fl_reference',1,'p_fl_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',796),\n  ('fl_reference -> if_function _trailers','fl_reference',2,'p_fl_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',797),\n  ('fl_reference -> iserr_function','fl_reference',1,'p_fl_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',798),\n  ('fl_reference -> iserror_function','fl_reference',1,'p_fl_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',799),\n  ('fl_reference -> fl_cell_range','fl_reference',1,'p_fl_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',800),\n  ('fl_reference -> fl_cell_range _trailers','fl_reference',2,'p_fl_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',801),\n  ('fl_reference -> fl_cell_reference','fl_reference',1,'p_fl_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',802),\n  ('fl_reference -> fl_cell_reference _trailers','fl_reference',2,'p_fl_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',803),\n  ('fl_reference -> fl_column_reference','fl_reference',1,'p_fl_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',804),\n  ('fl_reference -> fl_column_reference _trailers','fl_reference',2,'p_fl_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',805),\n  ('fl_reference -> fl_row_reference','fl_reference',1,'p_fl_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',806),\n  ('fl_reference -> fl_row_reference _trailers','fl_reference',2,'p_fl_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',807),\n  ('fl_reference -> fl_named_column_reference','fl_reference',1,'p_fl_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',808),\n  ('fl_reference -> fl_named_column_reference _trailers','fl_reference',2,'p_fl_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',809),\n  ('fl_reference -> fl_named_row_reference','fl_reference',1,'p_fl_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',810),\n  ('fl_reference -> fl_named_row_reference _trailers','fl_reference',2,'p_fl_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',811),\n  ('fl_reference -> fl_naked_worksheet_reference','fl_reference',1,'p_fl_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',812),\n  ('fl_reference -> fl_naked_worksheet_reference _trailers','fl_reference',2,'p_fl_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',813),\n  ('fl_reference -> fl_deleted_reference','fl_reference',1,'p_fl_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',814),\n  ('fl_reference -> fl_invalid_reference','fl_reference',1,'p_fl_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',815),\n  ('fl_reference -> fl_dde_call','fl_reference',1,'p_fl_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',816),\n  ('fl_dde_call -> SSHORTSTRING EXCLAMATION SSHORTSTRING','fl_dde_call',3,'p_fl_dde_call','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',828),\n  ('fl_cell_range -> fl_cell_reference COLON fl_cell_reference','fl_cell_range',3,'p_fl_cell_range','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',833),\n  ('fl_cell_range -> fl_cell_reference COLON fl_deleted_reference','fl_cell_range',3,'p_fl_cell_range','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',834),\n  ('fl_cell_range -> fl_deleted_reference COLON fl_cell_reference','fl_cell_range',3,'p_fl_cell_range','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',835),\n  ('fl_cell_range -> fl_deleted_reference COLON fl_deleted_reference','fl_cell_range',3,'p_fl_cell_range','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',836),\n  ('fl_cell_range -> fl_cell_reference COLON fl_invalid_reference','fl_cell_range',3,'p_fl_cell_range','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',837),\n  ('fl_cell_range -> fl_invalid_reference COLON fl_cell_reference','fl_cell_range',3,'p_fl_cell_range','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',838),\n  ('fl_cell_range -> fl_invalid_reference COLON fl_invalid_reference','fl_cell_range',3,'p_fl_cell_range','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',839),\n  ('fl_cell_reference -> FLCELLREFLIKENAME','fl_cell_reference',1,'p_fl_cell_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',891),\n  ('fl_cell_reference -> LONGCELLREFERENCE','fl_cell_reference',1,'p_fl_cell_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',892),\n  ('fl_cell_reference -> FLCELLREFLIKENAME EXCLAMATION FLCELLREFLIKENAME','fl_cell_reference',3,'p_fl_cell_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',893),\n  ('fl_cell_reference -> FLCOLUMNREFLIKENAME EXCLAMATION FLCELLREFLIKENAME','fl_cell_reference',3,'p_fl_cell_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',894),\n  ('fl_cell_reference -> FLROWREFLIKENAME EXCLAMATION FLCELLREFLIKENAME','fl_cell_reference',3,'p_fl_cell_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',895),\n  ('fl_cell_reference -> NAME EXCLAMATION FLCELLREFLIKENAME','fl_cell_reference',3,'p_fl_cell_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',896),\n  ('fl_cell_reference -> fl_naked_worksheet_reference EXCLAMATION FLCELLREFLIKENAME','fl_cell_reference',3,'p_fl_cell_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',897),\n  ('fl_column_reference -> FLCOLUMNREFLIKENAME','fl_column_reference',1,'p_fl_column_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',909),\n  ('fl_column_reference -> LONGCOLUMNREFERENCE','fl_column_reference',1,'p_fl_column_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',910),\n  ('fl_column_reference -> FLCELLREFLIKENAME EXCLAMATION FLCOLUMNREFLIKENAME','fl_column_reference',3,'p_fl_column_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',911),\n  ('fl_column_reference -> FLCOLUMNREFLIKENAME EXCLAMATION FLCOLUMNREFLIKENAME','fl_column_reference',3,'p_fl_column_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',912),\n  ('fl_column_reference -> FLROWREFLIKENAME EXCLAMATION FLCOLUMNREFLIKENAME','fl_column_reference',3,'p_fl_column_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',913),\n  ('fl_column_reference -> NAME EXCLAMATION FLCOLUMNREFLIKENAME','fl_column_reference',3,'p_fl_column_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',914),\n  ('fl_column_reference -> fl_naked_worksheet_reference EXCLAMATION FLCOLUMNREFLIKENAME','fl_column_reference',3,'p_fl_column_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',915),\n  ('fl_row_reference -> FLROWREFLIKENAME','fl_row_reference',1,'p_fl_row_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',928),\n  ('fl_row_reference -> LONGROWREFERENCE','fl_row_reference',1,'p_fl_row_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',929),\n  ('fl_row_reference -> FLCELLREFLIKENAME EXCLAMATION FLROWREFLIKENAME','fl_row_reference',3,'p_fl_row_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',930),\n  ('fl_row_reference -> FLCOLUMNREFLIKENAME EXCLAMATION FLROWREFLIKENAME','fl_row_reference',3,'p_fl_row_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',931),\n  ('fl_row_reference -> FLROWREFLIKENAME EXCLAMATION FLROWREFLIKENAME','fl_row_reference',3,'p_fl_row_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',932),\n  ('fl_row_reference -> NAME EXCLAMATION FLROWREFLIKENAME','fl_row_reference',3,'p_fl_row_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',933),\n  ('fl_row_reference -> fl_naked_worksheet_reference EXCLAMATION FLROWREFLIKENAME','fl_row_reference',3,'p_fl_row_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',934),\n  ('fl_named_column_reference -> FLNAMEDCOLUMNREFERENCE','fl_named_column_reference',1,'p_fl_named_column_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',944),\n  ('fl_named_column_reference -> LONGNAMEDCOLUMNREFERENCE','fl_named_column_reference',1,'p_fl_named_column_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',945),\n  ('fl_named_column_reference -> FLCELLREFLIKENAME EXCLAMATION FLNAMEDCOLUMNREFERENCE','fl_named_column_reference',3,'p_fl_named_column_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',946),\n  ('fl_named_column_reference -> FLCOLUMNREFLIKENAME EXCLAMATION FLNAMEDCOLUMNREFERENCE','fl_named_column_reference',3,'p_fl_named_column_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',947),\n  ('fl_named_column_reference -> FLROWREFLIKENAME EXCLAMATION FLNAMEDCOLUMNREFERENCE','fl_named_column_reference',3,'p_fl_named_column_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',948),\n  ('fl_named_column_reference -> NAME EXCLAMATION FLNAMEDCOLUMNREFERENCE','fl_named_column_reference',3,'p_fl_named_column_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',949),\n  ('fl_named_column_reference -> fl_naked_worksheet_reference EXCLAMATION FLNAMEDCOLUMNREFERENCE','fl_named_column_reference',3,'p_fl_named_column_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',950),\n  ('fl_named_row_reference -> FLNAMEDROWREFERENCE','fl_named_row_reference',1,'p_fl_named_row_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',960),\n  ('fl_named_row_reference -> LONGNAMEDROWREFERENCE','fl_named_row_reference',1,'p_fl_named_row_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',961),\n  ('fl_named_row_reference -> FLCELLREFLIKENAME EXCLAMATION FLNAMEDROWREFERENCE','fl_named_row_reference',3,'p_fl_named_row_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',962),\n  ('fl_named_row_reference -> FLCOLUMNREFLIKENAME EXCLAMATION FLNAMEDROWREFERENCE','fl_named_row_reference',3,'p_fl_named_row_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',963),\n  ('fl_named_row_reference -> FLROWREFLIKENAME EXCLAMATION FLNAMEDROWREFERENCE','fl_named_row_reference',3,'p_fl_named_row_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',964),\n  ('fl_named_row_reference -> NAME EXCLAMATION FLNAMEDROWREFERENCE','fl_named_row_reference',3,'p_fl_named_row_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',965),\n  ('fl_named_row_reference -> fl_naked_worksheet_reference EXCLAMATION FLNAMEDROWREFERENCE','fl_named_row_reference',3,'p_fl_named_row_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',966),\n  ('fl_deleted_reference -> FLDELETEDREFERENCE','fl_deleted_reference',1,'p_fl_deleted_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',971),\n  ('fl_deleted_reference -> LONGDELETEDREFERENCE','fl_deleted_reference',1,'p_fl_deleted_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',972),\n  ('fl_deleted_reference -> FLCELLREFLIKENAME EXCLAMATION FLDELETEDREFERENCE','fl_deleted_reference',3,'p_fl_deleted_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',973),\n  ('fl_deleted_reference -> FLCOLUMNREFLIKENAME EXCLAMATION FLDELETEDREFERENCE','fl_deleted_reference',3,'p_fl_deleted_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',974),\n  ('fl_deleted_reference -> FLROWREFLIKENAME EXCLAMATION FLDELETEDREFERENCE','fl_deleted_reference',3,'p_fl_deleted_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',975),\n  ('fl_deleted_reference -> NAME EXCLAMATION FLDELETEDREFERENCE','fl_deleted_reference',3,'p_fl_deleted_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',976),\n  ('fl_deleted_reference -> fl_naked_worksheet_reference EXCLAMATION FLDELETEDREFERENCE','fl_deleted_reference',3,'p_fl_deleted_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',977),\n  ('fl_invalid_reference -> FLINVALIDREFERENCE','fl_invalid_reference',1,'p_fl_invalid_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',991),\n  ('fl_invalid_reference -> LONGINVALIDREFERENCE','fl_invalid_reference',1,'p_fl_invalid_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',992),\n  ('fl_invalid_reference -> FLCELLREFLIKENAME EXCLAMATION FLINVALIDREFERENCE','fl_invalid_reference',3,'p_fl_invalid_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',993),\n  ('fl_invalid_reference -> FLCOLUMNREFLIKENAME EXCLAMATION FLINVALIDREFERENCE','fl_invalid_reference',3,'p_fl_invalid_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',994),\n  ('fl_invalid_reference -> FLROWREFLIKENAME EXCLAMATION FLINVALIDREFERENCE','fl_invalid_reference',3,'p_fl_invalid_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',995),\n  ('fl_invalid_reference -> NAME EXCLAMATION FLINVALIDREFERENCE','fl_invalid_reference',3,'p_fl_invalid_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',996),\n  ('fl_invalid_reference -> fl_naked_worksheet_reference EXCLAMATION FLINVALIDREFERENCE','fl_invalid_reference',3,'p_fl_invalid_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',997),\n  ('fl_naked_worksheet_reference -> LESSTHAN NAME GREATERTHAN','fl_naked_worksheet_reference',3,'p_fl_naked_worksheet_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',1011),\n  ('fl_naked_worksheet_reference -> LESSTHAN FLCELLREFLIKENAME GREATERTHAN','fl_naked_worksheet_reference',3,'p_fl_naked_worksheet_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',1012),\n  ('fl_naked_worksheet_reference -> FLNAKEDWORKSHEETREFERENCE','fl_naked_worksheet_reference',1,'p_fl_naked_worksheet_reference','/home/harry/workspace/dirigible-spreadsheet/python/dirigible/sheet/parser/grammar.py',1013),\n]\n"
  },
  {
    "path": "dirigible/sheet/parser/tokens.py",
    "content": "# Copyright (c) 2005-2009 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\nimport re\n\nfrom sheet.parser import FormulaError\n\n\n\n# Precedence:\n#  http://www.dabeaz.com/ply/ply.html#ply_nn4\n#  1. All tokens defined by functions are added in the same order as they appear in the lexer file.\n#  2. Tokens defined by strings are added next by sorting them in order of decreasing regular expression length (longer expressions are added first).\n#  i.e function at lower line number > function at higher line number > long regexp string > short regexp string\n\n\n## LEX rules\n\ntokens = (\n    \"AMPERSAND\",\n    \"AND\",\n    \"BACKWARDQUOTE\",\n    \"CIRCUMFLEX\",\n    \"COLON\",\n    \"COLONEQUALS\",\n    \"COMMA\",\n    \"DECINTEGER\",\n    \"DLONGSTRING\",\n    \"DOT\",\n    \"DOUBLESLASH\",\n    \"DOUBLESTAR\",\n    \"DSHORTSTRING\",\n    \"ELLIPSIS\",\n    \"EXCLAMATION\",\n    \"EQUALS\",\n    \"EQUALTO\",\n    \"FLCELLREFLIKENAME\",\n    \"FLCOLUMNREFLIKENAME\",\n    \"FLROWREFLIKENAME\",\n    \"FLDELETEDREFERENCE\",\n    \"FLINVALIDREFERENCE\",\n    \"FLNAKEDWORKSHEETREFERENCE\",\n    \"FLNAMEDCOLUMNREFERENCE\",\n    \"FLNAMEDROWREFERENCE\",\n    \"FLOATNUMBER\",\n    \"FOR\",\n    \"GREATEREQUAL\",\n    \"GREATERTHAN\",\n    \"HEXINTEGER\",\n    \"IF\",\n    \"IMAGINARY\",\n    \"IN\",\n    \"IS\",\n    \"ISERR\",\n    \"ISERROR\",\n    \"LAMBDA\",\n    \"LEFTBRACE\",\n    \"LEFTBRACKET\",\n    \"LEFTPAREN\",\n    \"LEFTSHIFT\",\n    \"LESSEQUAL\",\n    \"LESSTHAN\",\n    \"LITTLEARROW\",\n    \"LONGCELLREFERENCE\",\n    \"LONGCOLUMNREFERENCE\",\n    \"LONGNAMEDCOLUMNREFERENCE\",\n    \"LONGNAMEDROWREFERENCE\",\n    \"LONGROWREFERENCE\",\n    \"LONGDELETEDREFERENCE\",\n    \"LONGINVALIDREFERENCE\",\n    \"MINUS\",\n    \"MOD_ITERP\",\n    \"NAME\",\n    \"NOT\",\n    \"OBSOLETEUNEQUAL\",\n    \"OCTINTEGER\",\n    \"OR\",\n    \"PERCENT\",\n    \"PLUS\",\n    \"RIGHTBRACE\",\n    \"RIGHTBRACKET\",\n    \"RIGHTPAREN\",\n    \"RIGHTSHIFT\",\n    \"SLASH\",\n    \"SLONGSTRING\",\n    \"SSHORTSTRING\",\n    \"STAR\",\n    \"TILDE\",\n    \"UNEQUAL\",\n    \"VERTICALBAR\",\n)\n\n\n# Utility regexps\nWHITESPACE    = r'[ \\t]*'\nSTRINGPREFIX  = r'(?:[uUrR]|[uU][rR])?'\nPOINTFLOAT    = r'(?:[0-9]*\\.[0-9]+|[0-9]+\\.[0-9]*)'\nEXPONENTFLOAT = r'(?:[0-9]+|' + POINTFLOAT + ')[eE][+-]?[0-9]+'\nFLOATNUMBER   = r'(?:' + EXPONENTFLOAT + '|' + POINTFLOAT + ')'\nWORKSHEETNAME = r\"'(?:[^']|'')*'\" + WHITESPACE + '!' + WHITESPACE\nCELLREFLIKENAME = r'\\$?[A-Za-z]+\\$?[1-9][0-9]*'\nCOLUMNREFLIKENAME = r'\\$?[A-Za-z]+_'\nNAMEDCOLUMNREFERENCE = r'\\#(?:[^\\#]|\\#\\#)+\\#_'\nNAMEDROWREFERENCE = r'_\\#(?:[^\\#]|\\#\\#)+\\#'\nROWREFLIKENAME = r'_\\$?[1-9][0-9]*'\n\n# Regexps for lower=precedence tokens\nt_EXCLAMATION      = r'!' + WHITESPACE\nt_PLUS             = r'\\+' + WHITESPACE\nt_MINUS            = r'-' + WHITESPACE\nt_TILDE            = r'~' + WHITESPACE\nt_DOUBLESTAR       = r'\\*\\*' + WHITESPACE\nt_STAR             = r'\\*' + WHITESPACE\nt_SLASH            = r'/' + WHITESPACE\nt_DOUBLESLASH      = r'//' + WHITESPACE\nt_PERCENT          = r'\\%' + WHITESPACE\nt_MOD_ITERP        = r'\\%\\%' + WHITESPACE\nt_LEFTSHIFT        = r'<<' + WHITESPACE\nt_RIGHTSHIFT       = r'>>' + WHITESPACE\nt_AMPERSAND        = r'&' + WHITESPACE\nt_CIRCUMFLEX       = r'\\^' + WHITESPACE\nt_VERTICALBAR      = r'\\|' + WHITESPACE\nt_LESSTHAN         = r'<' + WHITESPACE\nt_GREATERTHAN      = r'>' + WHITESPACE\nt_EQUALTO          = r'==' + WHITESPACE\nt_COLONEQUALS      = r':=' + WHITESPACE\nt_GREATEREQUAL     = r'>=' + WHITESPACE\nt_LESSEQUAL        = r'<=' + WHITESPACE\nt_UNEQUAL          = r'!=' + WHITESPACE\nt_OBSOLETEUNEQUAL  = r'<>' + WHITESPACE\nt_LITTLEARROW      = r'->' + WHITESPACE\nt_COLON            = r':' + WHITESPACE\nt_LEFTPAREN        = r'\\(' + WHITESPACE\nt_RIGHTPAREN       = r'\\)' + WHITESPACE\nt_LEFTBRACE        = r'{' + WHITESPACE\nt_RIGHTBRACE       = r'}' + WHITESPACE\nt_LEFTBRACKET      = r'\\[' + WHITESPACE\nt_RIGHTBRACKET     = r'\\]' + WHITESPACE\nt_COMMA            = r',' + WHITESPACE\nt_EQUALS           = r'=' + WHITESPACE\nt_BACKWARDQUOTE    = r'`' + WHITESPACE\nt_DOT              = r'\\.' + WHITESPACE\nt_ELLIPSIS         = r'\\.\\.\\.' + WHITESPACE\n\nt_FLNAMEDCOLUMNREFERENCE = NAMEDCOLUMNREFERENCE + WHITESPACE\nt_FLDELETEDREFERENCE = r'\\#Deleted!' + WHITESPACE\nt_FLINVALIDREFERENCE = r'\\#Invalid!' + WHITESPACE\n\n\ndef TokenRule(docstring):\n    def decorator(f):\n        f.__doc__ = docstring\n        return f\n    return decorator\n\n# Functions for tokens (which get higher precedence)\n\n@TokenRule(r\"<'(?:[^']|'')*'>\" + WHITESPACE)\ndef t_FLNAKEDWORKSHEETREFERENCE(t) : return t\n\n@TokenRule(WORKSHEETNAME + CELLREFLIKENAME + WHITESPACE)\ndef t_LONGCELLREFERENCE(t): return t\n\n@TokenRule(WORKSHEETNAME + COLUMNREFLIKENAME + WHITESPACE)\ndef t_LONGCOLUMNREFERENCE(t): return t\n\n@TokenRule(WORKSHEETNAME + NAMEDCOLUMNREFERENCE + WHITESPACE)\ndef t_LONGNAMEDCOLUMNREFERENCE(t): return t\n\n@TokenRule(WORKSHEETNAME + NAMEDROWREFERENCE + WHITESPACE)\ndef t_LONGNAMEDROWREFERENCE(t): return t\n\n@TokenRule(WORKSHEETNAME + ROWREFLIKENAME + WHITESPACE)\ndef t_LONGROWREFERENCE(t): return t\n\n@TokenRule(WORKSHEETNAME + t_FLDELETEDREFERENCE)\ndef t_LONGDELETEDREFERENCE(t): return t\n\n@TokenRule(WORKSHEETNAME + t_FLINVALIDREFERENCE)\ndef t_LONGINVALIDREFERENCE(t): return t\n\n@TokenRule(r'(?:' + FLOATNUMBER + '|[0-9]+)[Jj]' + WHITESPACE)\ndef t_IMAGINARY(t): return t\n\n@TokenRule(FLOATNUMBER + WHITESPACE)\ndef t_FLOATNUMBER(t): return t\n\n@TokenRule(r'0[xX][0-9a-fA-F]+[Ll]?' + WHITESPACE)\ndef t_HEXINTEGER(t): return t\n\n@TokenRule(r'0[0-7]+[Ll]?' + WHITESPACE)\ndef t_OCTINTEGER(t): return t\n\n@TokenRule(r'(?:0|[1-9][0-9]*)[Ll]?' + WHITESPACE)\ndef t_DECINTEGER(t): return t\n\n@TokenRule(STRINGPREFIX + r\"'''(?:[^\\\\]|\\\\.)*'''\" + WHITESPACE)\ndef t_SLONGSTRING(t): return t\n\n@TokenRule(STRINGPREFIX + r'\"\"\"(?:[^\\\\]|\\\\.)*\"\"\"' + WHITESPACE)\ndef t_DLONGSTRING(t): return t\n\n@TokenRule(STRINGPREFIX + r\"'(?:[^\\\\'\\n]|\\\\.)*'\" + WHITESPACE)\ndef t_SSHORTSTRING(t): return t\n\n@TokenRule(STRINGPREFIX + r'\"(?:[^\\\\\"\\n]|\\\\.)*\"' + WHITESPACE)\ndef t_DSHORTSTRING(t): return t\n\n\nreserved = {\n    \"for\"    :   \"FOR\",\n    \"in\"     :   \"IN\",\n    \"is\"     :   \"IS\",\n    \"lambda\" :   \"LAMBDA\",\n    \"not\"    :   \"NOT\",\n}\n\ncase_insensitive_reserved = {\n    \"and\"    :   \"AND\",\n    \"if\"     :   \"IF\",\n    \"or\"     :   \"OR\",\n    \"iserror\": \"ISERROR\",\n    \"iserr\"  : \"ISERR\",\n}\n\nunimplemented_python_keywords = (\n    \"assert\", \"break\", \"class\", \"continue\", \"def\", \"del\", \"elif\", \"else\", \"except\", \"exec\",\n    \"finally\", \"from\", \"global\", \"import\", \"pass\", \"print\", \"raise\", \"return\", \"try\", \"while\"\n)\n\nDOT_RE = re.compile(t_DOT)\nFLCELLREFLIKENAME_RE = re.compile(r'^%s$' % CELLREFLIKENAME)\nFLCOLUMNREFLIKENAME_RE = re.compile(r'^%s$' % COLUMNREFLIKENAME)\nFLROWREFLIKENAME_RE = re.compile(r'^%s$' % ROWREFLIKENAME)\n\n\n\n@TokenRule(NAMEDROWREFERENCE + WHITESPACE)\ndef t_FLNAMEDROWREFERENCE(t):\n    # this must come before t_TEXT, otherwise t_TEXT will eat the leading underscore\n    return t\n\n\n@TokenRule(r'\\$?[a-zA-Z_][a-zA-Z_0-9\\$]*' + WHITESPACE)\ndef t_TEXT(t):\n    text = t.value.rstrip()\n    if FLCELLREFLIKENAME_RE.match(text):\n        t.type = \"FLCELLREFLIKENAME\"\n    elif FLCOLUMNREFLIKENAME_RE.match(text):\n        t.type = \"FLCOLUMNREFLIKENAME\"\n    elif FLROWREFLIKENAME_RE.match(text):\n        t.type = \"FLROWREFLIKENAME\"\n    elif '$' in text:\n        dollarError(t)\n    elif text.lower() in case_insensitive_reserved:\n        t.type = case_insensitive_reserved[text.lower()]\n    elif text.lower() in unimplemented_python_keywords:\n        keywordError(t)\n    else:\n        t.type = reserved.get(text, \"NAME\")\n    return t\n\n\ndef dollarError(t):\n    raise FormulaError(\"Error in formula at position %d: unexpected '%s'\"\n                       % (t.lexer.lexpos - len(t.value), t.value))\n\ndef keywordError(t):\n    raise FormulaError(\"Error in formula at position %d: '%s' is a reserved word\"\n                       % (t.lexer.lexpos - len(t.value), t.value))\n\ndef t_error(t):\n    raise FormulaError(\"Error in formula at position %d: unexpected '%s'\" % (t.lexer.lexpos + 1, t.value))\n"
  },
  {
    "path": "dirigible/sheet/rewrite_formula_offset_cell_references.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\nfrom __future__ import division\n\nfrom .parser import FormulaError\nfrom .parser.parse_node import ParseNode\nfrom .parser.parse_node_constructors import FLCellReference, FLCellRange\nfrom .parser.parser import parse\n\n\ndef rewrite_source_sheet_formulae_for_cut(worksheet, source_range, dest_col, dest_row):\n    column_offset = dest_col - source_range[0]\n    row_offset = dest_row - source_range[1]\n    for location, cell in worksheet.iteritems():\n        worksheet[location].formula = rewrite_formula(\n            cell.formula, column_offset, row_offset, True, source_range)\n\n\ndef rewrite_formula(\n    formula, column_offset, row_offset, is_cut, orig_bounds\n):\n    if formula is None or not formula.startswith('='):\n        return formula\n\n    def cell_in_original_bounds(column, row):\n        return (\n            orig_bounds[0] <= column <= orig_bounds[2] and\n            orig_bounds[1] <= row <= orig_bounds[3]\n        )\n\n    def range_in_original_bounds(left, top, right, bottom):\n        return (\n            cell_in_original_bounds(left, top) and\n            cell_in_original_bounds(right, bottom)\n        )\n\n    def offset_cell_reference(node):\n        orig_column, orig_row = node.coords\n\n        if is_cut and not cell_in_original_bounds(orig_column, orig_row):\n            return node\n        node.offset(column_offset, row_offset, move_absolute=is_cut)\n        return node\n\n\n    def offset_cell_range(node):\n        corner1_col, corner1_row = node.first_cell_reference.coords\n        corner2_col, corner2_row = node.second_cell_reference.coords\n\n        if is_cut and not range_in_original_bounds(\n            corner1_col, corner1_row, corner2_col, corner2_row\n        ):\n            return node\n\n        node.first_cell_reference.offset(column_offset, row_offset, move_absolute=is_cut)\n        node.second_cell_reference.offset(column_offset, row_offset, move_absolute=is_cut)\n        return node\n\n\n    def rewrite(node):\n        if isinstance(node, ParseNode):\n            if isinstance(node, FLCellRange):\n                return offset_cell_range(node)\n            if isinstance(node, FLCellReference):\n                return offset_cell_reference(node)\n            else:\n                node.children = [\n                    rewrite(child)\n                    for child in node.children\n                ]\n        return node\n\n    try:\n        return rewrite(parse(formula)).flatten()\n    except FormulaError:\n        return formula\n\n"
  },
  {
    "path": "dirigible/sheet/sheet.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\nimport json\nfrom textwrap import dedent\nfrom uuid import uuid4\n\nfrom django.db import models, transaction\nfrom django.contrib.auth.models import User\n\nfrom user.models import OneTimePad\nfrom .calculate import calculate_with_timeout\nfrom .worksheet import (\n    Worksheet, worksheet_from_json, worksheet_to_json\n)\n\n\nclass Sheet(models.Model):\n    last_modified = models.DateTimeField(auto_now=True)\n\n    version = models.IntegerField(default=0)\n\n    owner = models.ForeignKey(User)\n    name = models.TextField(default='Untitled')\n\n    width = models.IntegerField(default=52)\n    height = models.IntegerField(default=1000)\n\n    contents_json = models.TextField(default=worksheet_to_json(Worksheet()))\n\n    timeout_seconds = models.IntegerField(default=55)\n\n    is_public = models.BooleanField(default=False)\n    allow_json_api_access = models.BooleanField(default=False)\n    api_key = models.CharField(max_length=72)\n\n    column_widths_json = models.TextField(default='{}')\n\n    usercode = models.TextField(\n        default=dedent(\"\"\"\n            load_constants(worksheet)\n\n            # Put code here if it needs to access constants in the spreadsheet\n            # and to be accessed by the formulae.  Examples: imports,\n            # user-defined functions and classes you want to use in cells.\n\n            evaluate_formulae(worksheet)\n\n            # Put code here if it needs to access the results of the formulae.\n        \"\"\")\n    )\n\n\n    def __init__(self, *args, **kwargs):\n        models.Model.__init__(self, *args, **kwargs)\n        self.column_widths = json.loads(self.column_widths_json)\n        if not self.api_key:\n            self.api_key = str(uuid4())\n\n    def __unicode__(self):\n        return 'Sheet %d: %s' % (self.id, self.name)\n\n\n    def create_private_key(self):\n        self.otp = OneTimePad(user=self.owner)\n        self.otp.save()\n        return unicode(self.otp.guid)\n\n\n    def _delete_private_key(self):\n        self.otp.delete()\n\n\n    def save(self, *args, **kwargs):\n        if self.name == 'Untitled':\n            models.Model.save(self, *args, **kwargs) # save to set self.id\n            self.name = 'Sheet %d' % (self.id,)\n        self.column_widths_json = json.dumps(self.column_widths)\n        models.Model.save(self, *args, **kwargs)\n\n\n    def unjsonify_worksheet(self):\n        return worksheet_from_json(self.contents_json)\n\n\n    def jsonify_worksheet(self, worksheet):\n        self.contents_json = worksheet_to_json(worksheet)\n\n\n    def merge_non_calc_attrs(self, sheet_in_db):\n        self.name = sheet_in_db.name\n        self.column_widths = sheet_in_db.column_widths\n\n\n    def calculate(self):\n        private_key = self.create_private_key()\n        worksheet = self.unjsonify_worksheet()\n        transaction.commit()\n        try:\n            calculate_with_timeout(worksheet, self.usercode, self.timeout_seconds, private_key)\n        finally:\n            self._delete_private_key()\n        self.jsonify_worksheet(worksheet)\n\n\n"
  },
  {
    "path": "dirigible/sheet/templates/export_csv_error.html",
    "content": "{% extends \"error_page.html\" %}\n\n{% block title %}\n    CSV Export Error: Dirigible\n{% endblock %}\n\n{% block error_title %}Could not export CSV file{% endblock %}\n{% block error_text %}\nSorry, your spreadsheet contains characters that cannot be saved in Excel CSV format.<br />\nPlease try again using the international version. \nSee the <a href=\"/documentation/import_export.html\">documentation</a> for more info.\n{% endblock %}\n\n\n{% block error_recovery_link %}\n<a id=\"id_sheet_link\" href=\"{% url 'sheet_page' sheet.owner.username sheet.id %}\">Return to your sheet ({{sheet.name}})</a>\n{% endblock %}\n"
  },
  {
    "path": "dirigible/sheet/templates/import_csv_error.html",
    "content": "{% extends \"error_page.html\" %}\n\n{% block title %}\n    CSV Import Error: Dirigible\n{% endblock %}\n\n{% block error_title %}Could not import CSV file{% endblock %}\n{% block error_text %}\n    Sorry, the file you uploaded was not in a recognised CSV format\n{% endblock %}\n\n\n{% block error_recovery_link %}\n<a id=\"id_sheet_link\" href=\"{% url 'sheet_page' sheet.owner.username sheet.id %}\">Return to your sheet ({{sheet.name}})</a>\n{% endblock %}\n"
  },
  {
    "path": "dirigible/sheet/templates/import_xls_error.html",
    "content": "{% extends \"error_page.html\" %}\n\n{% block title %}\n    Excel Import Error: Dirigible\n{% endblock %}\n\n{% block error_title %}Could not import Excel file{% endblock %}\n{% block error_text %}\n    Sorry, the file you uploaded was not imported\n{% endblock %}\n\n\n{% block error_recovery_link %}\n<a id=\"id_account_link\" href=\"/\">Return to your account page</a>\n{% endblock %}\n"
  },
  {
    "path": "dirigible/sheet/templates/sheet_page.html",
    "content": "{% extends \"base.html\" %}\n\n{% block head %}\n\n    <link rel=\"stylesheet\" href=\"/static/dirigible/styles/sheet_page.css\" type=\"text/css\" media=\"screen\" charset=\"utf-8\" />\n    <link rel=\"stylesheet\" href=\"/static/slickgrid/slick.grid.css\" type=\"text/css\" media=\"screen\" charset=\"utf-8\" />\n\n    <script type=\"text/javascript\" src=\"/static/json/json2.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/jquery/jquery.event.drag-2.0.min.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/jquery/jquery.ajaxq-0.0.1.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/jquery/jquery.cookie.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/jquery/jeip.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/jquery/jquery.caret.1.02.min.js\"></script>\n\n    <script type=\"text/javascript\" src=\"/static/slickgrid/slick.core.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/slickgrid/slick.grid.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/slickgrid/slick.editors.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/slickgrid/slick.cellrangeselector.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/slickgrid/slick.cellrangedecorator.js\"></script>\n\n    <script src=\"/static/ace/ace-uncompressed.js\" type=\"text/javascript\" charset=\"utf-8\"></script>\n    <script src=\"/static/ace/mode-python.js\" type=\"text/javascript\" charset=\"utf-8\"></script>\n\n    <script type=\"text/javascript\" src=\"/static/splitter/splitter.js\"></script>\n\n    <script type=\"text/javascript\" src=\"/static/dirigible/scripts/cell_editor.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/dirigible/scripts/console_view.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/dirigible/scripts/dialogs.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/dirigible/scripts/editor_commands.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/dirigible/scripts/grid_commands.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/dirigible/scripts/grid_content_converter.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/dirigible/scripts/grid_interaction_handler.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/dirigible/scripts/grid_remote_model.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/dirigible/scripts/grid_view.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/dirigible/scripts/htmlescape.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/dirigible/scripts/keyboard_cellrange_selector.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/dirigible/scripts/page_interaction_handler.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/dirigible/scripts/page_view.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/dirigible/scripts/page_commands.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/dirigible/scripts/security_settings.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/dirigible/scripts/selection_model.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/dirigible/scripts/sheet_page_utils.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/dirigible/scripts/toolbar_interaction_handler.js\"></script>\n    <script type=\"text/javascript\" src=\"/static/dirigible/scripts/usercode_view.js\"></script>\n\n    <script type=\"text/javascript\">\n        // Replace console.log with something that does nothing\n        // to prevent a stray console.log from killing everything\n        if (!window.console || !window.console.log) {\n            window.console = {};\n            window.console.log = function() {};\n        }\n\n        $.extend(urls, {\n            importXls: '{% url 'sheet_import_xls' sheet.owner.username %}',\n            importCsv: '{% url 'sheet_import_csv' sheet.owner.username sheet.id %}',\n\n            calculate: \"{% url 'sheet_calculate' sheet.owner.username sheet.id %}\",\n            getJSONGridData: \"{% url 'sheet_get_json_grid_data_for_ui' sheet.owner.username sheet.id %}\",\n            getJSONMetaData: \"{% url 'sheet_get_json_meta_data_for_ui' sheet.owner.username sheet.id %}\",\n\n            setCellFormula: \"{% url 'sheet_set_cell_formula' sheet.owner.username sheet.id %}\",\n            setColumnWidths: \"{% url 'sheet_set_column_widths' sheet.owner.username sheet.id %}\",\n            setSecuritySettings: \"{% url 'sheet_set_sheet_security_settings' sheet.owner.username sheet.id %}\",\n\n            setSheetName: \"{% url 'sheet_set_sheet_name' sheet.owner.username sheet.id %}\",\n            setSheetUsercode: \"{% url 'sheet_set_sheet_usercode' sheet.owner.username sheet.id %}\",\n\n            cut: \"{% url 'sheet_cut' sheet.owner.username sheet.id %}\",\n            copy: \"{% url 'sheet_copy' sheet.owner.username sheet.id %}\",\n            paste: \"{% url 'sheet_paste' sheet.owner.username sheet.id %}\",\n            clearCells: \"{% url 'sheet_clear_cells' sheet.owner.username sheet.id %}\",\n\n            apiBase: \"{% url 'api_v0.1_get_sheet_json' sheet.owner.username sheet.id %}\"\n        });\n\n        var securitySettingsDialog = new Dirigible.SecuritySettings.Dialog($, urls);\n        var securitySettingsModel = new Dirigible.SecuritySettings.Model($, urls);\n        securitySettingsModel.apiKey = \"{{ sheet.api_key }}\";\n        securitySettingsModel.enabled = \"{{ sheet.allow_json_api_access }}\" === \"True\";\n        securitySettingsModel.isPublic = \"{{ sheet.is_public }}\" === \"True\";\n\n        var grid = null;\n        var editor = null;\n\n        $(function()\n        {\n            var editable = (\n                \"{{ sheet.public_view_mode }}\" === \"False\" ||\n                \"{{ user.is_staff }}\" === \"True\"\n            );\n            securitySettingsModel.updatePageUI();\n\n            editor = ace.edit(\"id_usercode\");\n            var usercodeView = new Dirigible.UsercodeView(editor);\n            usercodeView.initialiseComponents(editable);\n            var editorCommands = new Dirigible.EditorCommands(urls, editor);\n\n            var CellEditor = CellEditorFactory($('#id_formula_bar'));\n\n            var grid_options = {\n                enableColumnReorder: false,\n                enableCellNavigation: true,\n                shiftKeyPreventsNavigation: false,\n                editable: editable,\n                autoEdit: false,\n                alwaysNavigateDownOnCommit: true,\n                editorFactory: { getEditor : function(_) { return CellEditor; } }\n            };\n\n            var gridRemoteModel = new Dirigible.GridRemoteModel(urls);\n            grid = new Slick.Grid($(\"#id_grid\"), gridRemoteModel, [], grid_options);\n            grid.setSelectionModel(new Dirigible.SelectionModel());\n            grid.getCellFormula = function (slickCellLocation) {\n                return getGridCellFormula(grid, slickCellLocation);\n            };\n            var consoleView = new Dirigible.ConsoleView($('#id_console'));\n            var gridView = new Dirigible.GridView(grid, gridRemoteModel);\n            var pageView = new Dirigible.PageView(\n                '{{sheet.owner.username}}', consoleView, usercodeView, gridView\n            );\n            Dirigible.SheetUtils.Initialise(urls, pageView);\n            Dirigible.Dialogs.Initialise(grid, urls);\n\n            var gridCommands = new Dirigible.GridCommands(urls, grid);\n            var pageCommands = new Dirigible.PageCommands(gridCommands, editorCommands);\n\n            var toolbarIH = new Dirigible.ToolbarInteractionHandler(\n                gridCommands, pageCommands\n            );\n\n            var pageIH = new Dirigible.PageInteractionHandler(\n                $, pageView, pageCommands, editor, editorCommands, grid, gridCommands\n            );\n            pageIH.initialiseComponents(urls, editable);\n\n            var gridIH = new Dirigible.GridInteractionHandler(\n                $, grid, gridCommands\n            );\n\n            toolbarIH.bind();\n            pageIH.bind();\n            gridIH.bind();\n\n            $.getJSON(urls.getJSONMetaData, firstTimeUIInitialisation);\n\n            function firstTimeUIInitialisation(sheetMetaData) {\n                pageIH.resizeComponents();\n                pageView.updateMetaData(sheetMetaData);\n                var columns = Dirigible.GridView.jsonToSlickGridColumnHeaders(sheetMetaData);\n                self.grid.setColumns(columns);\n\n                if (window.document.activeElement !== $('#id_tutorial_promo_dialog_close')[0]) {\n                    grid.setActiveCell(0, 1);\n                }\n            }\n\n            /***** End definitions *****/\n\n            $('#id_spinner_image').ajaxStart(\n                function() { $(this).removeClass('hidden'); }\n            );\n            $('#id_spinner_image').ajaxStop(\n                function() { $(this).addClass('hidden'); }\n            );\n\n            $('#id_security_button').click(function() {\n                securitySettingsDialog.show(securitySettingsModel);\n            });\n\n            securitySettingsDialog.bind(securitySettingsModel);\n\n            {% if not profile.has_seen_sheet_page %}\n                $('#id_tutorial_promo_dialog_close').click( function() {\n                    $('#id_tutorial_promo_dialog').dialog(\"close\");\n                });\n                $('#id_tutorial_promo_dialog').dialog({ width: 533 });\n                $('#id_tutorial_promo_dialog_close').focus();\n            {% endif %}\n        });\n\n    </script>\n{% endblock %}\n\n{% block body %}\n    <div id=\"id_header_and_toolbar\">\n        <div id='id_header'>\n            <a href=\"/\">\n                <img id=\"id_small_header_logo\" src=\"/static/dirigible/images/logo_transparent_289x66.png\" width=\"30%\" alt=\"Dirigible logo\" title=\"Dirigible logo\" />\n            </a>\n            {% include \"header_links_include.html\" %}\n        </div>\n\n        {% if sheet.public_view_mode %}\n            {% if user.is_staff %}\n                <div id=\"id_copy_sheet_div\">\n                    <a id=\"id_copy_sheet_link\" href=\"{% url 'sheet_copy_sheet' sheet.owner.username sheet.id %}\">Click here to copy this sheet</a>.\n                </div>\n            {% else %}\n                <div id=\"id_copy_sheet_div\">\n                    This sheet is not editable because it belongs to someone else.<br/>\n                    <a id=\"id_copy_sheet_link\" href=\"{% url 'sheet_copy_sheet' sheet.owner.username sheet.id %}\">Click here to get your own copy that you can change</a>.\n                </div>\n            {% endif %}\n        {% endif %}\n\n        <div class=\"clear\"></div>\n\n        <div id=\"id_sheet_name_and_toolbar\">\n            <div id=\"id_sheet_name_wrap\">\n                <div id=\"id_sheet_name\"></div>\n            </div>\n            <div id=\"id_toolbar_wrap\">\n                <div id='id_toolbar'>\n                    <img src=\"/static/dirigible/images/spinner.gif\" alt=\"spinner\" id=\"id_spinner_image\" class=\"hidden\" />\n                    <div id='id_toolbar_buttons'>\n                    {% if sheet.public_view_mode and not user.is_staff %}\n                        <img id='id_export_button' alt=\"Download as CSV file\" title=\"Download as CSV file\" src='/static/dirigible/images/toolbar/export_button.png' />\n                    {% else %}\n                        <img id='id_import_button' alt=\"Import a file\" title=\"Import a file\" src='/static/dirigible/images/toolbar/import_button.png' />\n                        <img id='id_export_button' alt=\"Download as CSV file\" title=\"Download as CSV file\" src='/static/dirigible/images/toolbar/export_button.png' />\n                        <img id='id_cut_button' alt=\"Cut\" title=\"Cut\" src='/static/dirigible/images/toolbar/cut_button.png' />\n                        <img id='id_copy_button' alt=\"Copy\" title=\"Copy\" src='/static/dirigible/images/toolbar/copy_button.png' />\n                        <img id='id_paste_button' alt=\"Paste\" title=\"Paste\" src='/static/dirigible/images/toolbar/paste_button.png' />\n                        <img id='id_recalc_button' alt=\"Recalculate sheet (F9)\" title=\"Recalculate sheet (F9)\" src='/static/dirigible/images/toolbar/recalc_button.png' />\n                    {% endif %}\n\n                        <div id=\"id_buffering_message\" class=\"hidden\">buffering ...</div>\n                    </div>\n                </div>\n            </div>\n        </div>\n\n        <div class=\"clear\"></div>\n    </div>\n    <div id=\"id_grid_and_code\">\n        <div id='id_left_column'>\n            <div id=\"id_grid_and_formula_bar\">\n                {% if sheet.public_view_mode and not user.is_staff %}\n                    <input id=\"id_formula_bar\" type=\"text\" readonly />\n                {% else %}\n                    <input id=\"id_formula_bar\" type=\"text\" />\n                {% endif %}\n                <div id='id_grid'> </div>\n            </div>\n        </div>\n        <div id='id_right_column'>\n            <div id='id_usercode_wrap'>\n                <pre id='id_usercode' class='user_code'>{{sheet.usercode}}</pre>\n            </div>\n            <div id=\"id_console_wrap\">\n                <div id='id_console'></div>\n            </div>\n        </div>\n    </div>\n    {% if not profile.has_seen_sheet_page %}\n        <div id=\"id_tutorial_promo_dialog\" title=\"Welcome to Dirigible!\" style=\"display: none\">\n            <p>\n                Hi, thanks for trying out Dirigible.\n            </p>\n            <p>\n                We really, really recommend you <strong>\n                    <a href=\"/documentation/tutorial01.html\" id=\"id_tutorial_link\">check out our tutorial</a>\n                </strong>.\n                It'll only take you about ten minutes, and it will show you how to use all the AWESOME\n                Dirigible features, like scripting your spreadsheet in Python, using lists and objects\n                in cells, and defining your own functions.\n            </p>\n            <p>\n                Alternatively, if you're an expert Python hacker\n                and want to have a go at writing usercode right away, you may find the\n                <a href=\"/documentation/overview.html\">Technical Overview</a> the best place to start.\n            </p>\n            <p>\n                We won't bug you with this popup again (nary a paperclip in sight...)\n                At any time, you can get to all of the documentation by clicking the <strong>Help</strong>\n                link at the top right of the page.\n            </p>\n            <div class=\"dialog_buttons\">\n                <input type=\"button\" value=\"OK\" id=\"id_tutorial_promo_dialog_close\" />\n            </div>\n        </div>\n    {% endif %}\n    <div id=\"id_import_form\" title=\"Select the file to import:\" style=\"display: none\">\n        <form method=\"POST\" enctype=\"multipart/form-data\">\n            {% csrf_token %}\n            <p>\n                {% for field in import_form %}\n                    {{field}}\n                {% endfor %}\n            </p>\n            <span class=\"import_form_radio_button\" title=\"For csv files generated by, or for Excel (windows-1252 encoded)\">\n                Excel encoding<input type=\"radio\" name=\"csv_encoding\" value=\"excel\" checked />\n            </span>\n            <br />\n            <span class=\"import_form_radio_button\" title=\"For any other csv files, utf-8/unicode. Try this if the other doesn't work\">\n                Auto-detect other<input type=\"radio\" name=\"csv_encoding\" value=\"other\" />\n            </span>\n            <br />\n            <a href=\"/documentation/import_export.html\" target=\"_new\">Help</a>\n            <input type=\"submit\" value=\"Upload CSV\" id=\"id_import_form_upload_csv_button\" />\n            <input type=\"submit\" value=\"Upload Excel Values\" id=\"id_import_form_upload_xls_values_button\" />\n            <input type=\"button\" value=\"Cancel\" id=\"id_import_form_cancel_button\" />\n        </form>\n    </div>\n    <div id=\"id_export_dialog\" title=\"Choose CSV format:\" style=\"display: none\">\n        <ul>\n            <li>\n                <a id=\"id_export_csv_excel_version\" href=\"{% url 'sheet_export_csv' sheet.owner.username sheet.id 'excel' %}\">\n                    Download for Excel (default)\n                </a>\n            </li>\n            <li>\n                <a id=\"id_export_csv_unicode_version\" href=\"{% url 'sheet_export_csv' sheet.owner.username sheet.id 'unicode' %}\">\n                    Download as international (utf8-encoded) CSV\n                </a>\n        </li>\n        </ul>\n        <p>\n            You may need to use the international version if your sheet contains\n            non-western characters, but it may not import into Excel as easily.\n            See the <a href=\"/documentation/import_export.html\" target=\"_new\">documentation</a> for more info.\n        </p>\n        <div class=\"dialog_buttons\">\n            <input type=\"button\" value=\"Close\" id=\"id_export_dialog_close_button\" />\n        </div>\n    </div>\n    <div id=\"id_security_form\" title=\"Sheet Security Settings\" style=\"display:none\">\n        <form action=\"\">\n            {% csrf_token %}\n            <p>\n                <div id=\"id_security_form_general_settings\">\n                    <input\n                        type=\"checkbox\"\n                        id=\"id_security_form_public_sheet_checkbox\"\n                        name=\"public_sheet\"\n                        title=\"Make sheet publicly-visible\"\n                    />\n                    <label for=\"id_security_form_public_sheet_checkbox\">Make sheet public</label><br/>\n                    <a href='/documentation/public_sheets.html' target='_blank' id='id_security_form_public_sheet_help'>[What is this?]</a>\n                </div>\n\n                <div id=\"id_security_form_api_settings\">\n                    <input\n                        type=\"checkbox\"\n                        id=\"id_security_form_json_enabled_checkbox\"\n                        name=\"allow_api_access\"\n                        title=\"Allow JSON API Access\"\n                    />\n                    <label for=\"id_security_form_json_enabled_checkbox\">Allow JSON API access</label><br/>\n                    <a href='/documentation/json_api.html' target='_blank' id='id_security_form_json_help'>[What is this?]</a>\n                    <div id=\"id_security_form_api_subsettings\">\n                        <table>\n                            <tr>\n                                <td class=\"label\">Sheet API key:</td>\n                                <td><input id=\"id_security_form_json_api_key\" /></td>\n                            </tr>\n                            <tr class=\"label\">\n                                <td>Sheet API URL:</td>\n                                <td><input id=\"id_security_form_json_api_url\" readonly /></td>\n                            </tr>\n                        </table>\n                    </div>\n                </div>\n                <div class=\"clear\" />\n            </p>\n            <br />\n\n            <div class=\"clear\" />\n\n            <div class=\"dialog_buttons\">\n                <input type=\"button\" value=\"OK\" id=\"id_security_form_ok_button\" />\n                <input type=\"button\" value=\"Cancel\" id=\"id_security_form_cancel_button\" />\n            </div>\n\n            <div class=\"clear\" />\n\n            <div id=\"id_security_form_save_error\">\n                Could not save security settings to server, please try again in a few moments...\n            </div>\n        </div>\n        </form>\n    </div>\n{% endblock %}\n"
  },
  {
    "path": "dirigible/sheet/tests/__init__.py",
    "content": "\n"
  },
  {
    "path": "dirigible/sheet/tests/parser/__init__.py",
    "content": ""
  },
  {
    "path": "dirigible/sheet/tests/parser/test_fl_cell_range_parse_node.py",
    "content": "# Copyright (c) 2005-2009 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\ntry:\n    import unittest2 as unittest\nexcept ImportError:\n    import unittest\n\nfrom sheet.parser.fl_cell_range_parse_node import FLCellRangeParseNode\nfrom sheet.parser.parse_node import ParseNode\nfrom sheet.parser.fl_cell_reference_parse_node import FLCellReferenceParseNode\n\nclass FLCellRangeParseNodeTest(unittest.TestCase):\n\n    def testConstructor(self):\n        flCellRange = FLCellRangeParseNode([\"A1\", \":\", \"D2\"])\n        self.assertEquals(flCellRange.type, ParseNode.FL_CELL_RANGE,\n            \"Node was of the wrong type\")\n        self.assertEquals(flCellRange.children, [\"A1\", \":\", \"D2\"],\n            \"Node had the wrong children\")\n\n    def testStr(self):\n        node = FLCellRangeParseNode([\"a1\", \":\", \"g8\"])\n        self.assertEquals(str(node),\n            \"<FLCellRangeParseNode type=\\\"FL_CELL_RANGE\\\" children=['a1', ':', 'g8']>\",\n            \"Wrong string representation\")\n\n\n    def testRegisteredWithParse(self):\n        \"test registered with ParseNode\"\n        self.assertEquals(\n            type(ParseNode.construct_node(\n                ParseNode.FL_CELL_RANGE, ['a1', ':', 'b4'])),\n            FLCellRangeParseNode,\n            \"Class is not registered with ParseNode\")\n\n\n    def testCellReferences(self):\n        first = FLCellReferenceParseNode(['a1'])\n        second = FLCellReferenceParseNode(['g8'])\n        node = FLCellRangeParseNode([first, \":\", second])\n\n        self.assertEquals(node.first_cell_reference, first)\n        self.assertEquals(node.second_cell_reference, second)\n\n        another = FLCellReferenceParseNode(['c2'])\n        node.first_cell_reference = another\n        self.assertEquals(node.first_cell_reference, another)\n\n        node.second_cell_reference = another\n        self.assertEquals(node.second_cell_reference, another)\n\n\n    def testColon(self):\n        first = FLCellReferenceParseNode(['a1'])\n        second = FLCellReferenceParseNode(['g8'])\n        node = FLCellRangeParseNode([first, \":\", second])\n        self.assertEquals(node.colon, \":\")\n\n\n\n"
  },
  {
    "path": "dirigible/sheet/tests/parser/test_fl_cell_reference_parse_node.py",
    "content": "# Copyright (c) 2005-2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\ntry:\n    import unittest2 as unittest\nexcept ImportError:\n    import unittest\n\nfrom sheet.parser.parse_node import ParseNode\nfrom sheet.parser.fl_cell_reference_parse_node import FLCellReferenceParseNode\nfrom sheet.parser.fl_reference_parse_node import FLReferenceParseNode\n\nclass FLCellReferenceParseNodeTest(unittest.TestCase):\n\n    def testConstructor(self):\n        flCellReference = FLCellReferenceParseNode([\"A1\"])\n        self.assertTrue(isinstance(flCellReference, FLReferenceParseNode), 'should be a parse node')\n        self.assertEquals(flCellReference.type, ParseNode.FL_CELL_REFERENCE, \"Node was of the wrong type\")\n        self.assertEquals(flCellReference.children, [\"A1\"], \"Node had the wrong children\")\n\n\n    def testStr(self):\n        node = FLCellReferenceParseNode([\"a1\"])\n        self.assertEquals(str(node), \"<FLCellReferenceParseNode type=\\\"FL_CELL_REFERENCE\\\" children=['a1']>\", \"Wrong string representation\")\n\n\n    def testColAbsolute(self):\n        self.assertFalse(FLCellReferenceParseNode([\"A1\"]).colAbsolute, \"Incorrect colAbsolute for A1\")\n        self.assertFalse(FLCellReferenceParseNode([\"A$1\"]).colAbsolute, \"Incorrect colAbsolute for A$1\")\n        self.assertTrue(FLCellReferenceParseNode([\"$A1\"]).colAbsolute, \"Incorrect colAbsolute for $A1\")\n        self.assertTrue(FLCellReferenceParseNode([\"$A$1\"]).colAbsolute, \"Incorrect colAbsolute for $A$1\")\n\n        self.assertFalse(FLCellReferenceParseNode([\"SheetSomething\", \"! \", \"A1\"]).colAbsolute,\n                         \"Incorrect colAbsolute for A1 with worksheet\")\n        self.assertTrue(FLCellReferenceParseNode([\"SheetSomething\", \"! \", \"$A$1\"]).colAbsolute,\n                         \"Incorrect colAbsolute for $A$1 with worksheet\")\n\n    def testRowAbsolute(self):\n        self.assertFalse(FLCellReferenceParseNode([\"A1\"]).rowAbsolute, \"Incorrect rowAbsolute for A1\")\n        self.assertTrue(FLCellReferenceParseNode([\"A$1\"]).rowAbsolute, \"Incorrect rowAbsolute for A$1\")\n        self.assertFalse(FLCellReferenceParseNode([\"$A1\"]).rowAbsolute, \"Incorrect rowAbsolute for $A1\")\n        self.assertTrue(FLCellReferenceParseNode([\"$A$1\"]).rowAbsolute, \"Incorrect rowAbsolute for $A$1\")\n\n        self.assertFalse(FLCellReferenceParseNode([\"SheetSomething\", \"! \", \"A1\"]).rowAbsolute,\n                         \"Incorrect colAbsolute for A1 with worksheet\")\n        self.assertTrue(FLCellReferenceParseNode([\"SheetSomething\", \"! \", \"$A$1\"]).rowAbsolute,\n                         \"Incorrect colAbsolute for $A$1 with worksheet\")\n\n\n    def testPlainCellName(self):\n        self.assertEquals(FLCellReferenceParseNode([\"A1\"]).plainCellName, \"A1\", \"Incorrect plainCellName for A1\")\n        self.assertEquals(FLCellReferenceParseNode([\"A$1\"]).plainCellName, \"A1\", \"Incorrect plainCellName for A$1\")\n        self.assertEquals(FLCellReferenceParseNode([\"$A1\"]).plainCellName, \"A1\", \"Incorrect plainCellName for $A1\")\n        self.assertEquals(FLCellReferenceParseNode([\"$A$1\"]).plainCellName, \"A1\", \"Incorrect plainCellName for $A$1\")\n\n        self.assertEquals(FLCellReferenceParseNode([\"SheetSomething\", \"! \", \"A1\"]).plainCellName, \"A1\",\n                          \"Incorrect plainCellName for A1 with worksheet\")\n        self.assertEquals(FLCellReferenceParseNode([\"SheetSomething\", \"! \", \"$A$1\"]).plainCellName, \"A1\",\n                          \"Incorrect plainCellName for $A$1 with worksheet\")\n\n\n    def testRegisteredWithParse(self):\n        \"test registered with ParseNode\"\n        self.assertEquals(type(ParseNode.construct_node(ParseNode.FL_CELL_REFERENCE, ['A1'])), FLCellReferenceParseNode,\n                          \"Class is not registered with ParseNode\")\n\n\n    def testCellProperty(self):\n        node = FLCellReferenceParseNode([\"G8  \"])\n        self.assertEquals(node.localReference, \"G8  \", \"cellref wrong\")\n\n        node = FLCellReferenceParseNode([\"Sheet1\", \"!\", \"G8  \"])\n        self.assertEquals(node.localReference, \"G8  \", \"cellref wrong\")\n\n        node = FLCellReferenceParseNode([\"G8   \"])\n        node.localReference = \"F5\"\n        self.assertEquals(node.localReference, \"F5\", \"should discard whitespace\")\n\n        node = FLCellReferenceParseNode([\"G8  \"])\n        node.localReference = \"F5  \"\n        self.assertEquals(node.localReference, \"F5  \", \"should not pile whitespace\")\n\n\n    def testCanonicalise(self):\n        node = FLCellReferenceParseNode([\"bertie \", \"!\", \"a1  \"])\n        node.canonicalise(['Bertie'])\n        self.assertEquals(node.localReference, 'A1  ')\n        self.assertEquals(node.worksheetReference, 'Bertie')\n\n\n    def testOffset(self):\n        node = FLCellReferenceParseNode([\"G8  \"])\n        node.offset(1, 4)\n        self.assertEquals(node.localReference, \"H12  \", \"offset didnt work\")\n\n        node = FLCellReferenceParseNode([\"G8  \"])\n        node.offset(-7, 1)\n        self.assertEquals(node.localReference, \"#Invalid!  \", \"offset didnt work\")\n\n        node = FLCellReferenceParseNode([\"G8  \"])\n        node.offset(1, -8)\n        self.assertEquals(node.localReference, \"#Invalid!  \", \"offset didnt work\")\n\n        node = FLCellReferenceParseNode([\"G8  \"])\n        node.offset(-6, -7)\n        self.assertEquals(node.localReference, \"A1  \", \"offset didnt work\")\n\n        node = FLCellReferenceParseNode([\"$G8  \"])\n        node.offset(-6, -7)\n        self.assertEquals(node.localReference, \"$G1  \", \"offset didnt work\")\n\n        node = FLCellReferenceParseNode([\"G$8  \"])\n        node.offset(-6, -7)\n        self.assertEquals(node.localReference, \"A$8  \", \"offset didnt work\")\n\n        node = FLCellReferenceParseNode([\"$G$8  \"])\n        node.offset(-6, -7)\n        self.assertEquals(node.localReference, \"$G$8  \", \"offset didnt work\")\n\n        node = FLCellReferenceParseNode([\"$G$8  \"])\n        node.offset(-6, -7, move_absolute=True)\n        self.assertEquals(node.localReference, \"$A$1  \", \"offset didnt work\")\n\n        node = FLCellReferenceParseNode([\"ZZZ9  \"])\n        node.offset(1, -1)\n        self.assertEquals(node.localReference, \"#Invalid!  \", \"offset didnt work\")\n\n\n    def testCoords(self):\n        node = FLCellReferenceParseNode([\"A2\"])\n        self.assertEquals(node.coords, (1, 2))\n\n        node = FLCellReferenceParseNode([\"B1\"])\n        self.assertEquals(node.coords, (2, 1))\n\n"
  },
  {
    "path": "dirigible/sheet/tests/parser/test_fl_coloumn_reference_parse_node.py",
    "content": "# Copyright (c) 2005-2009 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\ntry:\n    import unittest2 as unittest\nexcept ImportError:\n    import unittest\n\nfrom sheet.parser.parse_node import ParseNode\nfrom sheet.parser.fl_column_reference_parse_node import FLColumnReferenceParseNode\nfrom sheet.parser.fl_reference_parse_node import FLReferenceParseNode\n\nclass FLColumnReferenceParseNodeTest(unittest.TestCase):\n\n    def testConstructor(self):\n        flColumnReference = FLColumnReferenceParseNode([\"A_\"])\n        self.assertTrue(isinstance(flColumnReference, FLReferenceParseNode), 'should be a parse node')\n        self.assertEquals(flColumnReference.type, ParseNode.FL_COLUMN_REFERENCE, \"Node was of the wrong type\")\n        self.assertEquals(flColumnReference.children, [\"A_\"], \"Node had the wrong children\")\n\n\n    def testStr(self):\n        node = FLColumnReferenceParseNode([\"a_\"])\n        self.assertEquals(str(node), \"<FLColumnReferenceParseNode type=\\\"FL_COLUMN_REFERENCE\\\" children=['a_']>\", \"Wrong string representation\")\n\n\n    def testIsAbsolute(self):\n        self.assertFalse(FLColumnReferenceParseNode([\"A_\"]).isAbsolute, \"Incorrect isAbsolute for A_\")\n        self.assertTrue(FLColumnReferenceParseNode([\"$A_\"]).isAbsolute, \"Incorrect isAbsolute for $A_\")\n        self.assertTrue(FLColumnReferenceParseNode([\" $A_\"]).isAbsolute, \"Incorrect isAbsolute for  $A_\")\n\n        self.assertFalse(FLColumnReferenceParseNode([\"SheetSomething\", \"! \", \"A_\"]).isAbsolute,\n                         \"Incorrect isAbsolute for A_ with worksheet\")\n        self.assertTrue(FLColumnReferenceParseNode([\"SheetSomething\", \"! \", \"$A_\"]).isAbsolute,\n                         \"Incorrect isAbsolute for $A_ with worksheet\")\n\n\n    def testPlainColumnName(self):\n        self.assertEquals(FLColumnReferenceParseNode([\"A_\"]).plainColumnName, \"A\", \"Incorrect plainColumnName for A_\")\n        self.assertEquals(FLColumnReferenceParseNode([\"$A_\"]).plainColumnName, \"A\", \"Incorrect plaiaColumName for $A_\")\n\n        self.assertEquals(FLColumnReferenceParseNode([\"SheetSomething\", \"! \", \"A_\"]).plainColumnName, \"A\",\n                          \"Incorrect plainColumnName for A_ with worksheet\")\n        self.assertEquals(FLColumnReferenceParseNode([\"SheetSomething\", \"! \", \"$A_\"]).plainColumnName, \"A\",\n                          \"Incorrect plainColumnName for $A_ with worksheet\")\n\n\n    def testRegisteredWithParse(self):\n        \"test registered with ParseNode\"\n        self.assertEquals(type(ParseNode.construct_node(ParseNode.FL_COLUMN_REFERENCE, ['A_'])), FLColumnReferenceParseNode,\n                          \"Class is not registered with ParseNode\")\n\n\n    def testSettingPlainColumnName(self):\n        column = FLColumnReferenceParseNode([\"A_ \"])\n        column.plainColumnName = \"C\"\n        self.assertEquals(column.localReference, \"C_ \")\n\n        column = FLColumnReferenceParseNode([\"$A_ \"])\n        column.plainColumnName = \"C\"\n        self.assertEquals(column.localReference, \"$C_ \")\n\n\n    def testColumnReferenceProperty(self):\n        self.assertEquals(FLColumnReferenceParseNode([\"A_\"]).localReference, \"A_\")\n        self.assertEquals(FLColumnReferenceParseNode([\"ws1\", \"!\", \"A_\"]).localReference, \"A_\")\n\n\n    def testSettingColumnProperty(self):\n        column = FLColumnReferenceParseNode([\"A_ \"])\n        column.localReference = \"12345\"\n        self.assertEquals(column.localReference, \"12345\")\n\n        column = FLColumnReferenceParseNode([\"$A_ \"])\n        column.localReference = \"#Deleted!\"\n        self.assertEquals(column.localReference, \"#Deleted!\")\n\n\n    def testColIndexProperty(self):\n        self.assertEquals(FLColumnReferenceParseNode([\"D_\"]).colIndex, 4)\n        self.assertEquals(FLColumnReferenceParseNode([\"$B_\"]).colIndex, 2)\n\n\n    def testCanonicalise(self):\n        node = FLColumnReferenceParseNode([\"sheet1  \", \"!\", \"$d_\"])\n        node.canonicalise(['Sheet1'])\n        self.assertEquals(node.localReference, '$D_')\n        self.assertEquals(node.worksheetReference, 'Sheet1')\n\n\n    def testOffset(self):\n        node = FLColumnReferenceParseNode(['B_ '])\n        node.offset(0, 1000)\n        self.assertEquals(node.localReference, 'B_ ')\n        node.offset(1, -1000)\n        self.assertEquals(node.localReference, 'C_ ')\n        node.offset(-2, 0)\n        self.assertEquals(node.localReference, 'A_ ')\n        node.offset(-1, 1337)\n        self.assertEquals(node.localReference, '#Invalid! ')\n\n\n    def testOffsetAbsoluteRefs(self):\n        node = FLColumnReferenceParseNode(['$C_ '])\n        node.offset(54, 0xDEADBEEF)\n        self.assertEquals(node.localReference, '$C_ ')\n\n        node.offset(4, 0xDEADBEEF, moveAbsolute=True)\n        self.assertEquals(node.localReference, '$G_ ')\n\n\n    def testCoords(self):\n        node = FLColumnReferenceParseNode(['$A_ '])\n        self.assertEquals(node.coords, (1, 0))\n\n        node = FLColumnReferenceParseNode(['B_ '])\n        self.assertEquals(node.coords, (2, 0))\n\n"
  },
  {
    "path": "dirigible/sheet/tests/parser/test_fl_named_column_reference_parse_node.py",
    "content": "# Copyright (c) 2005-2009 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\ntry:\n    import unittest2 as unittest\nexcept ImportError:\n    import unittest\n\nfrom sheet.parser.parse_node import ParseNode\nfrom sheet.parser.fl_named_column_reference_parse_node import FLNamedColumnReferenceParseNode\n\n\nclass FLNamedColumnReferenceParseNodeTest(unittest.TestCase):\n\n    def testConstruct(self):\n        pn = FLNamedColumnReferenceParseNode([\"#foo#_\"])\n        self.assertEquals(pn.children, [\"#foo#_\"])\n        self.assertEquals(pn.type, ParseNode.FL_NAMED_COLUMN_REFERENCE)\n\n\n    def testColumnReference(self):\n        pn1 = FLNamedColumnReferenceParseNode([\"#foo###_   \"])\n        pn2 = FLNamedColumnReferenceParseNode([\"blah\", \"!\", \"#foo###_  \"])\n        self.assertEquals(pn1.header, \"foo#\")\n        self.assertEquals(pn2.header, \"foo#\")\n\n\n    def testRegisteredWithParse(self):\n        \"test registered with ParseNode\"\n        self.assertEquals(type(ParseNode.construct_node(ParseNode.FL_NAMED_COLUMN_REFERENCE, ['dfytjdky'])),\n                          FLNamedColumnReferenceParseNode,\n                          \"Class is not registered with ParseNode\")\n"
  },
  {
    "path": "dirigible/sheet/tests/parser/test_fl_named_row_reference_parse_node.py",
    "content": "# Copyright (c) 2005-2009 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\ntry:\n    import unittest2 as unittest\nexcept ImportError:\n    import unittest\n\nfrom sheet.parser.parse_node import ParseNode\nfrom sheet.parser.fl_named_row_reference_parse_node import FLNamedRowReferenceParseNode\n\n\nclass FLNamedRowReferenceParseNodeTest(unittest.TestCase):\n\n    def testConstruct(self):\n        pn = FLNamedRowReferenceParseNode([\"#foo#_\"])\n        self.assertEquals(pn.children, [\"#foo#_\"])\n        self.assertEquals(pn.type, ParseNode.FL_NAMED_ROW_REFERENCE)\n\n\n    def testRowReference(self):\n        pn1 = FLNamedRowReferenceParseNode([\"_#foo###   \"])\n        pn2 = FLNamedRowReferenceParseNode([\"blah\", \"!\", \"_#foo###  \"])\n        self.assertEquals(pn1.header, \"foo#\")\n        self.assertEquals(pn2.header, \"foo#\")\n\n\n    def testRegisteredWithParse(self):\n        \"test registered with ParseNode\"\n        self.assertEquals(type(ParseNode.construct_node(ParseNode.FL_NAMED_ROW_REFERENCE, ['dfytjdky'])), FLNamedRowReferenceParseNode,\n                          \"Class is not registered with ParseNode\")\n\n"
  },
  {
    "path": "dirigible/sheet/tests/parser/test_fl_reference_parse_node.py",
    "content": "# Copyright (c) 2005-2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\ntry:\n    import unittest2 as unittest\nexcept ImportError:\n    import unittest\n\nfrom mock import sentinel\n\nfrom sheet.parser.fl_reference_parse_node import FLReferenceParseNode, quote_fl_worksheet_name, unquote_fl_worksheet_name\n\nclass FLReferenceParseNodeTest(unittest.TestCase):\n\n    def testWorksheetReferenceProperty(self):\n        ref = FLReferenceParseNode(None, [sentinel.onlyChild])\n        self.assertEquals(ref.worksheetReference, None)\n\n        ref = FLReferenceParseNode(None, [\"'Thin Lizzy'    \", sentinel.second, sentinel.third])\n        self.assertEquals(ref.children[0], \"'Thin Lizzy'    \")\n        self.assertEquals(ref.worksheetReference, 'Thin Lizzy')\n\n        # loses the whitespace information\n        ref.worksheetReference = None\n        self.assertEquals(ref.children, [sentinel.third])\n\n        # therefore no whitespace information here\n        ref.worksheetReference = \"Ben Folds' Five    \"\n        self.assertEquals(ref.children, [\"'Ben Folds'' Five    '\", '!', sentinel.third])\n        self.assertEquals(ref.worksheetReference, \"Ben Folds' Five    \")\n\n        ref.children[0] = \"'with trailing whitespace'   \"\n        ref.worksheetReference = \"Shihad\"\n        self.assertEquals(ref.children, [\"Shihad   \", '!', sentinel.third])\n\n\n    def testInit(self):\n        self.assertRaises(AssertionError, FLReferenceParseNode, sentinel.type, [sentinel.firstChild, sentinel.secondChild])\n        self.assertRaises(AssertionError, FLReferenceParseNode, sentinel.type, [sentinel.child] * 4)\n\n        ref = FLReferenceParseNode(sentinel.type, [sentinel.firstChild])\n        self.assertEquals(ref.type, sentinel.type)\n        self.assertEquals(ref.children, [sentinel.firstChild])\n\n        ref = FLReferenceParseNode(sentinel.type, [sentinel.firstChild, sentinel.secondChild, sentinel.thirdChild])\n        self.assertEquals(ref.type, sentinel.type)\n        self.assertEquals(ref.children, [sentinel.firstChild, sentinel.secondChild, sentinel.thirdChild])\n\n\n    def testCanonicalise(self):\n        def assertCanon(reference, worksheetNames, expected):\n            ref = FLReferenceParseNode(None, [reference, '!', sentinel.onlyChild])\n            ref.canonicalise(worksheetNames)\n            self.assertEquals(ref.children[0], expected)\n\n        assertCanon('foo', ['foO'], 'foO')\n        assertCanon('foo', [], 'foo')\n        assertCanon(\"'''s'\", [\"'S\"], \"'''S'\")\n        assertCanon(\"foo  \", [], 'foo  ')\n\n        ref = FLReferenceParseNode(None, ['A1'])\n        ref.canonicalise([])\n        self.assertEquals(ref.worksheetReference, None)\n\n\n    def testQuoteFLWorksheetName(self):\n        self.assertEquals(quote_fl_worksheet_name(\"Wyborowa\"), \"Wyborowa\", \"Incorrectly quoted\")\n        self.assertEquals(quote_fl_worksheet_name(\"#3\"), \"'#3'\", \"Incorrectly quoted special character\")\n        self.assertEquals(quote_fl_worksheet_name(\"Let's do it.\"), \"'Let''s do it.'\", \"Incorrectly quoted single quote\")\n        self.assertEquals(quote_fl_worksheet_name(\"31337\"), \"'31337'\", \"Incorrectly quoted numbers only\")\n        self.assertEquals(quote_fl_worksheet_name(\"D31337\"), \"D31337\", \"Incorrectly quoted cell-ref-like-name\")\n        self.assertEquals(quote_fl_worksheet_name(\"Testing 123\"), \"'Testing 123'\", \"Incorrectly quoted name\")\n        self.assertEquals(quote_fl_worksheet_name(\"  raz dwa trzy  \"), \"'  raz dwa trzy  '\", \"Incorrectly quoted name with spaces\")\n\n\n    def testUnquoteFLWorksheetName(self):\n        self.assertEquals(unquote_fl_worksheet_name(\"Wyborowa\"), \"Wyborowa\", \"Incorrectly unquoted\")\n        self.assertEquals(unquote_fl_worksheet_name(\"'#3'\"), \"#3\", \"Incorrectly unquoted special character\")\n        self.assertEquals(unquote_fl_worksheet_name(\"'Let''s do it.'\"), \"Let's do it.\", \"Incorrectly unquoted single quote\")\n        self.assertEquals(unquote_fl_worksheet_name(\"'31337'\"), \"31337\", \"Incorrectly unquoted numbers only\")\n        self.assertEquals(unquote_fl_worksheet_name(\"D31337\"), \"D31337\", \"Incorrectly unquoted cell-ref-like-name\")\n        self.assertEquals(unquote_fl_worksheet_name(\"'Testing 123'\"), \"Testing 123\", \"Incorrectly unquoted name\")\n        self.assertEquals(unquote_fl_worksheet_name(\"'  raz dwa trzy  '\"), \"  raz dwa trzy  \", \"Incorrectly unquoted name with spaces\")\n"
  },
  {
    "path": "dirigible/sheet/tests/parser/test_fl_row_reference_parse_node.py",
    "content": "# Copyright (c) 2005-2009 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\ntry:\n    import unittest2 as unittest\nexcept ImportError:\n    import unittest\n\nfrom sheet.parser.parse_node import ParseNode\nfrom sheet.parser.fl_row_reference_parse_node import FLRowReferenceParseNode\nfrom sheet.parser.fl_reference_parse_node import FLReferenceParseNode\n\n\nclass FLRowReferenceParseNodeTest(unittest.TestCase):\n\n    def testConstructor(self):\n        flRowReference = FLRowReferenceParseNode([\"_1\"])\n        self.assertTrue(isinstance(flRowReference, FLReferenceParseNode), 'should be a parse node')\n        self.assertEquals(flRowReference.type, ParseNode.FL_ROW_REFERENCE, \"Node was of the wrong type\")\n        self.assertEquals(flRowReference.children, [\"_1\"], \"Node had the wrong children\")\n\n\n    def testStr(self):\n        node = FLRowReferenceParseNode([\"_1\"])\n        self.assertEquals(str(node), \"<FLRowReferenceParseNode type=\\\"FL_ROW_REFERENCE\\\" children=['_1']>\", \"Wrong string representation\")\n\n\n    def testIsAbsolute(self):\n        self.assertFalse(FLRowReferenceParseNode([\"_1\"]).isAbsolute, \"Incorrect isAbsolute for _1\")\n        self.assertTrue(FLRowReferenceParseNode([\"_$1\"]).isAbsolute, \"Incorrect isAbsolute for _$1\")\n\n        self.assertFalse(FLRowReferenceParseNode([\"SheetSomething\", \"! \", \"_1\"]).isAbsolute,\n                         \"Incorrect isAbsolute for _1 with worksheet\")\n        self.assertTrue(FLRowReferenceParseNode([\"SheetSomething\", \"! \", \"_$1\"]).isAbsolute,\n                         \"Incorrect isAbsolute for _$1 with worksheet\")\n\n\n    def testPlainRowName(self):\n        self.assertEquals(FLRowReferenceParseNode([\"_1\"]).plainRowName, \"1\", \"Incorrect plainRowName for _1\")\n        self.assertEquals(FLRowReferenceParseNode([\"_$1\"]).plainRowName, \"1\", \"Incorrect plaiaRowName for _$1\")\n\n        self.assertEquals(FLRowReferenceParseNode([\"SheetSomething\", \"! \", \"_1\"]).plainRowName, \"1\",\n                          \"Incorrect plainRowName for _1 with worksheet\")\n        self.assertEquals(FLRowReferenceParseNode([\"SheetSomething\", \"! \", \"_$1\"]).plainRowName, \"1\",\n                          \"Incorrect plainRowName for _$1 with worksheet\")\n\n\n    def testRegisteredWithParse(self):\n        \"test registered with ParseNode\"\n        self.assertEquals(type(ParseNode.construct_node(ParseNode.FL_ROW_REFERENCE, [])), FLRowReferenceParseNode,\n                          \"Class is not registered with ParseNode\")\n\n\n    def testSettingPlainRowName(self):\n        row = FLRowReferenceParseNode([\"_1 \"])\n        row.plainRowName = \"3\"\n        self.assertEquals(row.localReference, \"_3 \")\n\n        row = FLRowReferenceParseNode([\"_$1 \"])\n        row.plainRowName = \"3\"\n        self.assertEquals(row.localReference, \"_$3 \")\n\n\n    def testRowReferenceProperty(self):\n        self.assertEquals(FLRowReferenceParseNode([\"_1\"]).localReference, \"_1\")\n        self.assertEquals(FLRowReferenceParseNode([\"ws1\", \"!\", \"_1\"]).localReference, \"_1\")\n\n\n    def testSettingRowProperty(self):\n        row = FLRowReferenceParseNode([\"_1 \"])\n        row.localReference = \"12345\"\n        self.assertEquals(row.localReference, \"12345\")\n\n        row = FLRowReferenceParseNode([\"_$1 \"])\n        row.localReference = \"#Deleted!\"\n        self.assertEquals(row.localReference, \"#Deleted!\")\n\n\n    def testRowIndexProperty(self):\n        self.assertEquals(FLRowReferenceParseNode([\"_4\"]).rowIndex, 4)\n        self.assertEquals(FLRowReferenceParseNode([\"_$2\"]).rowIndex, 2)\n\n\n    def testOffset(self):\n        node = FLRowReferenceParseNode(['_2 '])\n        node.offset(1000, 0)\n        self.assertEquals(node.localReference, '_2 ')\n        node.offset(-1000, 1)\n        self.assertEquals(node.localReference, '_3 ')\n        node.offset(0, -2)\n        self.assertEquals(node.localReference, '_1 ')\n        node.offset(1337, -1)\n        self.assertEquals(node.localReference, '#Invalid! ')\n\n\n    def testOffsetAbsoluteRefs(self):\n        node = FLRowReferenceParseNode(['_$3 '])\n        node.offset(0xDEADBEEF, 54)\n        self.assertEquals(node.localReference, '_$3 ')\n\n        node.offset(0xDEADBEEF, 4, moveAbsolute=True)\n        self.assertEquals(node.localReference, '_$7 ')\n\n\n    def testCoords(self):\n        node = FLRowReferenceParseNode(['_$1 '])\n        self.assertEquals(node.coords, (0, 1))\n\n        node = FLRowReferenceParseNode(['_2 '])\n        self.assertEquals(node.coords, (0, 2))\n\n"
  },
  {
    "path": "dirigible/sheet/tests/parser/test_parse_node.py",
    "content": "# Copyright (c) 2005-2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\ntry:\n    import unittest2 as unittest\nexcept ImportError:\n    import unittest\n\nfrom sheet.parser.parse_node import ParseNode\n\n\ndef Expr(children):\n    return ParseNode.construct_node(ParseNode.EXPR, children)\n\ndef Name(children):\n    return ParseNode.construct_node(ParseNode.NAME, children)\n\ndef ShiftExpr(children):\n    return ParseNode.construct_node(ParseNode.SHIFT_EXPR, children)\n\n\nclass ParseNodeTest(unittest.TestCase):\n\n    def testStr(self):\n        node = Expr([Name([\"a\"])])\n        self.assertEquals(str(node), \"<ParseNode type=\\\"EXPR\\\" children=[<ParseNode type=\\\"NAME\\\" children=['a']>]>\", \"Wrong string representation\")\n\n\n    def testEquals(self):\n        node1 = Expr([Name([\"a\"])])\n        node2 = Expr([Name([\"a\"])])\n        self.assertTrue(node1 == node2, \"Nodes expected to be equal were not\")\n        self.assertFalse(node1 != node2, \"Nodes not expected to be unequal were\")\n\n        node3 = Expr([Name([\"b\"])])\n        self.assertFalse(node2 == node3, \"Nodes not expected to be equal were\")\n        self.assertTrue(node2 != node3, \"Nodes expected to be unequal were not\")\n\n        node4 = Expr([Name(5)])\n        self.assertFalse(node3 == None, \"Nodes not expected to be equal were (None)\")\n        self.assertFalse(node3 == node4, \"Nodes not expected to be equal were (unsized)\")\n        self.assertFalse(node4 == node3, \"Nodes not expected to be equal were (unsized)\")\n        self.assertFalse(node4 == \"hello\", \"Nodes not expected to be equal were (type)\")\n\n    def testFlatten(self):\n        node = Expr([None])\n        self.assertEquals(node.flatten(), \"\", \"Unexpected flattening\")\n\n        node = Expr([Name([\"a\"])])\n        self.assertEquals(node.flatten(), \"a\", \"Unexpected flattening\")\n\n        node = Expr([Name([\"a \"]), \"or \", ShiftExpr([Name([\"b \"]), \"<< \", Name([\"c\"])])])\n        self.assertEquals(node.flatten(), \"a or b << c\", \"Unexpected flattening\")\n\n\n    def testFlattenWithUnicode(self):\n        node = Expr([Name([u\"\\u20ac\"])])\n        self.assertEquals(node.flatten(), u\"\\u20ac\", \"unexpected flattening\")\n\n\n    def testFlattenHandlesSubclassed(self):\n        \"test flatten handles subclassed parse nodes\"\n\n        class ParseNodeSubclass(ParseNode):\n            pass\n\n        node = Expr([ParseNodeSubclass(ParseNode.NAME, [\"Thing\"])])\n\n        self.assertEquals(node.flatten(), \"Thing\", \"Thing should have been thing. Merry Christmas!\")\n\n\n    def testParseNodeShould(self):\n        \"test ParseNode should allow registering of node classes\"\n        class CustomParseNode(ParseNode):\n            def __init__(self, children):\n                ParseNode.__init__(self, \"CUSTOM_NODE_TYPE\", children)\n\n        ParseNode.register_node_type(\"CUSTOM_NODE_TYPE\", CustomParseNode)\n        try:\n\n            node = ParseNode.construct_node(\"CUSTOM_NODE_TYPE\", [])\n            self.assertEquals(type(node), CustomParseNode, \"ParseNode.construct_node didn't dispatch to CustomParseNode\")\n            self.assertEquals(node.type, \"CUSTOM_NODE_TYPE\", \"Wrong type attribute\")\n            self.assertEquals(node.children, [], \"Wrong children\")\n\n\n            node = ParseNode.construct_node(\"FISH BOWL\", [\"gox blamp\"])\n            self.assertEquals(type(node), ParseNode, \"ParseNode.construct_node didn't fall back to ParseNode\")\n            self.assertEquals(node.type, \"FISH BOWL\", \"Wrong type attribute\")\n            self.assertEquals(node.children, [\"gox blamp\"], \"Wrong children\")\n\n\n            self.assertIsNotNone(ParseNode.classRegistry, \"ParseNode didn't have a class registry\")\n        finally:\n            del ParseNode.classRegistry[\"CUSTOM_NODE_TYPE\"]\n\n"
  },
  {
    "path": "dirigible/sheet/tests/parser/test_parse_node_constructors.py",
    "content": "# Copyright (c) 2005-2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\ntry:\n    import unittest2 as unittest\nexcept ImportError:\n    import unittest\n\nfrom sheet.parser.fl_cell_reference_parse_node import FLCellReferenceParseNode\nfrom sheet.parser.fl_cell_range_parse_node import FLCellRangeParseNode\nfrom sheet.parser.fl_column_reference_parse_node import FLColumnReferenceParseNode\nfrom sheet.parser.fl_row_reference_parse_node import FLRowReferenceParseNode\nfrom sheet.parser.parse_node import ParseNode\nfrom sheet.parser.parse_node_constructors import (\n    AndTest,\n    ArgList,\n    Argument,\n    ArithExpr,\n    Atom,\n    Comparison,\n    CompOperator,\n    ConcatExpr,\n    DictMaker,\n    Expr,\n    ExprList,\n    Factor,\n    FLCellRange,\n    FLCellReference,\n    FLColumnReference,\n    FLRowReference,\n    FLDDECall,\n    FLDeletedReference,\n    FLInvalidReference,\n    FLNakedWorksheetReference,\n    FLReference,\n    FLRoot,\n    FPDef,\n    FPList,\n    GenFor,\n    GenIf,\n    GenIter,\n    LambDef,\n    ListFor,\n    ListIf,\n    ListIter,\n    ListMaker,\n    Name,\n    NotTest,\n    Number,\n    Power,\n    ShiftExpr,\n    SliceOp,\n    StringLiteral,\n    Subscript,\n    SubscriptList,\n    Term,\n    Test,\n    TestList,\n    TestListGexp,\n    Trailer,\n    VarArgsList,\n\n    ArithExpr_Term,\n    Expr_ConcatExpr_ShiftExpr,\n    Factor_Power_FLReference_Atom,\n    Test_AndTest_NotTest_Comparison,\n\n    ExprFromNameChild,\n    ExprFromAtomChild,\n    TestFromAtomChild,\n    TestFromPowerChild,\n)\n\n\nclass ParseNodeConstructorsTest(unittest.TestCase):\n\n    def testCombinedConvenienceConstructors(self):\n        randomChild = \"hello\"\n\n        self.assertEquals(ParseNode(ParseNode.ARITH_EXPR,\n                            [ParseNode(ParseNode.TERM, [randomChild])]),\n                          ArithExpr_Term(randomChild))\n\n        self.assertEquals(ParseNode(ParseNode.EXPR,\n                            [ParseNode(ParseNode.CONCAT_EXPR,\n                                [ParseNode(ParseNode.SHIFT_EXPR,\n                                    [randomChild])])]),\n                          Expr_ConcatExpr_ShiftExpr(randomChild))\n\n        self.assertEquals(ParseNode(ParseNode.FACTOR,\n                            [ParseNode(ParseNode.POWER,\n                                [ParseNode(ParseNode.FL_REFERENCE,\n                                    [ParseNode(ParseNode.ATOM,\n                                        [randomChild])])])]),\n                          Factor_Power_FLReference_Atom(randomChild))\n\n        self.assertEquals(ParseNode(ParseNode.TEST,\n                            [ParseNode(ParseNode.AND_TEST,\n                                [ParseNode(ParseNode.NOT_TEST,\n                                    [ParseNode(ParseNode.COMPARISON,\n                                        [randomChild])])])]),\n                          Test_AndTest_NotTest_Comparison(randomChild))\n\n        self.assertEquals(ParseNode(ParseNode.EXPR,\n                            [ParseNode(ParseNode.CONCAT_EXPR,\n                                [ParseNode(ParseNode.SHIFT_EXPR,\n                                    [ParseNode(ParseNode.ARITH_EXPR,\n                                        [ParseNode(ParseNode.TERM,\n                                            [ParseNode(ParseNode.FACTOR,\n                                                [ParseNode(ParseNode.POWER,\n                                                    [ParseNode(ParseNode.FL_REFERENCE,\n                                                        [ParseNode(ParseNode.ATOM,\n                                                            [ParseNode(ParseNode.NAME,\n                                                                [randomChild])])])])])])])])])]),\n                          ExprFromNameChild(randomChild))\n\n        self.assertEquals(ParseNode(ParseNode.EXPR,\n                            [ParseNode(ParseNode.CONCAT_EXPR,\n                                [ParseNode(ParseNode.SHIFT_EXPR,\n                                    [ParseNode(ParseNode.ARITH_EXPR,\n                                        [ParseNode(ParseNode.TERM,\n                                            [ParseNode(ParseNode.FACTOR,\n                                                [ParseNode(ParseNode.POWER,\n                                                    [ParseNode(ParseNode.FL_REFERENCE,\n                                                        [ParseNode(ParseNode.ATOM,\n                                                            [randomChild])])])])])])])])]),\n                          ExprFromAtomChild(randomChild))\n\n        self.assertEquals(ParseNode(ParseNode.TEST,\n                            [ParseNode(ParseNode.AND_TEST,\n                                [ParseNode(ParseNode.NOT_TEST,\n                                    [ParseNode(ParseNode.COMPARISON,\n                                        [ParseNode(ParseNode.EXPR,\n                                            [ParseNode(ParseNode.CONCAT_EXPR,\n                                                [ParseNode(ParseNode.SHIFT_EXPR,\n                                                    [ParseNode(ParseNode.ARITH_EXPR,\n                                                        [ParseNode(ParseNode.TERM,\n                                                            [ParseNode(ParseNode.FACTOR,\n                                                                [ParseNode(ParseNode.POWER,\n                                                                    [ParseNode(ParseNode.FL_REFERENCE,\n                                                                        [ParseNode(ParseNode.ATOM,\n                                                                            [randomChild])])])])])])])])])])])])]),\n                          TestFromAtomChild(randomChild))\n\n        self.assertEquals(ParseNode(ParseNode.TEST,\n                            [ParseNode(ParseNode.AND_TEST,\n                                [ParseNode(ParseNode.NOT_TEST,\n                                    [ParseNode(ParseNode.COMPARISON,\n                                        [ParseNode(ParseNode.EXPR,\n                                            [ParseNode(ParseNode.CONCAT_EXPR,\n                                                [ParseNode(ParseNode.SHIFT_EXPR,\n                                                    [ParseNode(ParseNode.ARITH_EXPR,\n                                                        [ParseNode(ParseNode.TERM,\n                                                            [ParseNode(ParseNode.FACTOR,\n                                                                [ParseNode(ParseNode.POWER,\n                                                                            [randomChild])])])])])])])])])])]),\n                          TestFromPowerChild(randomChild))\n\n\n\n    def testSimpleConvenienceConstructors(self):\n        randomList = [\"foo\", \"bar\", 27]\n\n        self.assertEquals(ParseNode(ParseNode.AND_TEST, randomList),\n                          AndTest(randomList))\n        self.assertEquals(ParseNode(ParseNode.ARG_LIST, randomList),\n                          ArgList(randomList))\n        self.assertEquals(ParseNode(ParseNode.ARGUMENT, randomList),\n                          Argument(randomList))\n        self.assertEquals(ParseNode(ParseNode.ARITH_EXPR, randomList),\n                          ArithExpr(randomList))\n        self.assertEquals(ParseNode(ParseNode.ATOM, randomList),\n                          Atom(randomList))\n        self.assertEquals(ParseNode(ParseNode.COMPARISON, randomList),\n                          Comparison(randomList))\n        self.assertEquals(ParseNode(ParseNode.COMP_OPERATOR, randomList),\n                          CompOperator(randomList))\n        self.assertEquals(ParseNode(ParseNode.CONCAT_EXPR, randomList),\n                          ConcatExpr(randomList))\n        self.assertEquals(ParseNode(ParseNode.DICT_MAKER, randomList),\n                          DictMaker(randomList))\n        self.assertEquals(ParseNode(ParseNode.EXPR, randomList),\n                          Expr(randomList))\n        self.assertEquals(ParseNode(ParseNode.EXPR_LIST, randomList),\n                          ExprList(randomList))\n        self.assertEquals(ParseNode(ParseNode.FACTOR, randomList),\n                          Factor(randomList))\n        self.assertEquals(FLCellRangeParseNode(randomList),\n                          FLCellRange(randomList))\n        self.assertEquals(FLCellReferenceParseNode(randomList),\n                          FLCellReference(randomList))\n        self.assertEquals(FLColumnReferenceParseNode(randomList),\n                          FLColumnReference(randomList))\n        self.assertEquals(FLRowReferenceParseNode(randomList),\n                          FLRowReference(randomList))\n        self.assertEquals(ParseNode(ParseNode.FL_DDE_CALL, randomList),\n                          FLDDECall(randomList))\n        self.assertEquals(ParseNode(ParseNode.FL_DELETED_REFERENCE, randomList),\n                          FLDeletedReference(randomList))\n        self.assertEquals(ParseNode(ParseNode.FL_INVALID_REFERENCE, randomList),\n                          FLInvalidReference(randomList))\n        self.assertEquals(ParseNode(ParseNode.FL_NAKED_WORKSHEET_REFERENCE, randomList),\n                          FLNakedWorksheetReference(randomList))\n        self.assertEquals(ParseNode(ParseNode.FL_REFERENCE, randomList),\n                          FLReference(randomList))\n        self.assertEquals(ParseNode(ParseNode.FL_ROOT, randomList),\n                          FLRoot(randomList))\n        self.assertEquals(ParseNode(ParseNode.FP_DEF, randomList),\n                          FPDef(randomList))\n        self.assertEquals(ParseNode(ParseNode.FP_LIST, randomList),\n                          FPList(randomList))\n        self.assertEquals(ParseNode(ParseNode.GEN_FOR, randomList),\n                          GenFor(randomList))\n        self.assertEquals(ParseNode(ParseNode.GEN_IF, randomList),\n                          GenIf(randomList))\n        self.assertEquals(ParseNode(ParseNode.GEN_ITER, randomList),\n                          GenIter(randomList))\n        self.assertEquals(ParseNode(ParseNode.LAMBDEF, randomList),\n                          LambDef(randomList))\n        self.assertEquals(ParseNode(ParseNode.LIST_FOR, randomList),\n                          ListFor(randomList))\n        self.assertEquals(ParseNode(ParseNode.LIST_IF, randomList),\n                          ListIf(randomList))\n        self.assertEquals(ParseNode(ParseNode.LIST_ITER, randomList),\n                          ListIter(randomList))\n        self.assertEquals(ParseNode(ParseNode.LIST_MAKER, randomList),\n                          ListMaker(randomList))\n        self.assertEquals(ParseNode(ParseNode.NAME, randomList),\n                          Name(randomList))\n        self.assertEquals(ParseNode(ParseNode.NOT_TEST, randomList),\n                          NotTest(randomList))\n        self.assertEquals(ParseNode(ParseNode.NUMBER, randomList),\n                          Number(randomList))\n        self.assertEquals(ParseNode(ParseNode.POWER, randomList),\n                          Power(randomList))\n        self.assertEquals(ParseNode(ParseNode.SHIFT_EXPR, randomList),\n                          ShiftExpr(randomList))\n        self.assertEquals(ParseNode(ParseNode.SLICE_OP, randomList),\n                          SliceOp(randomList))\n        self.assertEquals(ParseNode(ParseNode.STRINGLITERAL, randomList),\n                          StringLiteral(randomList))\n        self.assertEquals(ParseNode(ParseNode.SUBSCRIPT, randomList),\n                          Subscript(randomList))\n        self.assertEquals(ParseNode(ParseNode.SUBSCRIPT_LIST, randomList),\n                          SubscriptList(randomList))\n        self.assertEquals(ParseNode(ParseNode.TERM, randomList),\n                          Term(randomList))\n        self.assertEquals(ParseNode(ParseNode.TEST, randomList),\n                          Test(randomList))\n        self.assertEquals(ParseNode(ParseNode.TEST_LIST, randomList),\n                          TestList(randomList))\n        self.assertEquals(ParseNode(ParseNode.TEST_LIST_GEXP, randomList),\n                          TestListGexp(randomList))\n        self.assertEquals(ParseNode(ParseNode.TRAILER, randomList),\n                          Trailer(randomList))\n        self.assertEquals(ParseNode(ParseNode.VAR_ARGS_LIST, randomList),\n                          VarArgsList(randomList))\n\n\n    def testSubclasses(self):\n        self.assertNotEqual(type(FLCellReferenceParseNode([\"A1\"])),\n                            ParseNode,\n                            \"Subclass of ParseNode had incorrect type\")\n\n        self.assertNotEqual(type(FLColumnReferenceParseNode(['A_'])),\n                            ParseNode,\n                            \"Subclass of ParseNode had incorrect type\")\n\n        self.assertNotEqual(type(FLRowReferenceParseNode([])),\n                            ParseNode,\n                            \"Subclass of ParseNode had incorrect type\")\n\n\n    def testFLCell(self):\n        \"test FLCellReferenceParseNode registered\"\n        self.assertIn(ParseNode.FL_COLUMN_REFERENCE, ParseNode.classRegistry,\n                      \"FL_COLUMN_REFERENCE not registered in ParseNode class registry\")\n        self.assertIn(ParseNode.FL_ROW_REFERENCE, ParseNode.classRegistry,\n                      \"FL_ROW_REFERENCE registered in ParseNode class registry\")\n        self.assertIn(ParseNode.FL_CELL_REFERENCE, ParseNode.classRegistry,\n                      \"FL_CELL_REFERENCE not registered in ParseNode class registry\")\n\n"
  },
  {
    "path": "dirigible/sheet/tests/parser/test_parser.py",
    "content": "# Copyright (c) 2005-2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\ntry:\n    import unittest2 as unittest\nexcept ImportError:\n    import unittest\n\nfrom itertools import takewhile\nimport os\nfrom threading import Lock, Thread\nimport time\n\nfrom sheet.parser import FormulaError\nfrom sheet.parser.parser import parse\nfrom sheet.parser.parse_node import ParseNode\nfrom sheet.parser.parse_node_constructors import (\n    AndTest,\n    ArgList,\n    Argument,\n    ArithExpr,\n    Atom,\n    ConcatExpr,\n    FLCellRange,\n    FLCellReference,\n    FLDDECall,\n    FLDeletedReference,\n    FLInvalidReference,\n    FLNakedWorksheetReference,\n    FLReference,\n    FLRoot,\n    Comparison,\n    CompOperator,\n    DictMaker,\n    Expr,\n    ExprList,\n    Factor,\n    FPDef,\n    FPList,\n    GenFor,\n    GenIf,\n    GenIter,\n    LambDef,\n    ListFor,\n    ListIf,\n    ListIter,\n    ListMaker,\n    Name,\n    NotTest,\n    Number,\n    Percent,\n    Power,\n    ShiftExpr,\n    StringLiteral,\n    Subscript,\n    SubscriptList,\n    Term,\n    Test,\n    TestList,\n    TestListGexp,\n    Trailer,\n    VarArgsList,\n\n    Factor_Power_FLReference_Atom,\n    ArithExpr_Term,\n    Expr_ConcatExpr_ShiftExpr,\n    Test_AndTest_NotTest_Comparison,\n    ExprFromAtomChild,\n    ExprFromNameChild,\n    TestFromAtomChild,\n)\n\ndef ArgListElement(element):\n    if isinstance(element, str) or element.type is ParseNode.ARGUMENT:\n        return element\n    if element.type in (ParseNode.FL_CELL_REFERENCE, ParseNode.FL_NAKED_WORKSHEET_REFERENCE):\n        return Argument([Test_AndTest_NotTest_Comparison(\n                Expr_ConcatExpr_ShiftExpr(\n                    ArithExpr_Term(Factor([Power([FLReference([element])])]))))])\n    return Argument([element])\n\n\ndef TestFromFlReferenceChildren(children):\n    return Test_AndTest_NotTest_Comparison(\n                            Expr_ConcatExpr_ShiftExpr(\n                                ArithExpr_Term(\n                                    Factor([Power([FLReference(children)])])\n                   )))\n\n\ndef CreateTest(name, lBracket, argList, rBracket):\n    return TestFromFlReferenceChildren(\n        [\n        Atom([Name([name])]),\n        Trailer(\n            [\n            lBracket,\n            ArgList([ArgListElement(arg) for arg in argList]),\n            rBracket\n            ])\n        ]\n)\n\n\ndef ArgumentFromFLReferenceChild(child):\n    return Argument([Test_AndTest_NotTest_Comparison(\n                        Expr_ConcatExpr_ShiftExpr(\n                            ArithExpr_Term(Factor([Power([FLReference([child])])]))))])\n\ndef CreateAndTest(operand1, operator, operand2):\n    return Test(\n        [AndTest(\n                    [\n                    NotTest(\n                        [Comparison(\n                            [Expr_ConcatExpr_ShiftExpr(\n                                ArithExpr_Term(\n                                    Factor_Power_FLReference_Atom(operand1)))])]),\n                    operator,\n                    NotTest(\n                        [Comparison(\n                            [Expr_ConcatExpr_ShiftExpr(\n                                ArithExpr_Term(\n                                    Factor_Power_FLReference_Atom(operand2)))])])\n                    ])])\n\ndef CreateComparison(operand1, operator, operand2):\n    return Test(\n            [AndTest(\n                [NotTest(\n                    [Comparison(\n                        [\n                        Expr_ConcatExpr_ShiftExpr(\n                            ArithExpr_Term(\n                                Factor_Power_FLReference_Atom(operand1)))\n                        ,\n                        CompOperator([operator]),\n                        Expr_ConcatExpr_ShiftExpr(\n                            ArithExpr_Term(\n                                Factor_Power_FLReference_Atom(operand2)))\n                        ])])])])\n\ndef CreateParseTree(lBracket, test, forStr, exprList, inStr, testListSafe, rBracket):\n    return FLRoot([\"=\", Test_AndTest_NotTest_Comparison(\n            Expr_ConcatExpr_ShiftExpr(\n                ArithExpr_Term(\n                    Factor([Power([FLReference([Atom(\n                        [\n                        lBracket,\n                        ListMaker(\n                            [\n                            test,\n                            ListFor(\n                                [\n                                forStr,\n                                exprList,\n                                inStr,\n                                testListSafe\n                                ])\n                            ]),\n                        rBracket\n                        ])])])]))))])\n\n\nclass ParseTreeTest(unittest.TestCase):\n\n    def assertParsesToTree(self, text, expectedTree):\n        self.assertEquals(parse(text), expectedTree, \"\\nParsed >>>%s<<< incorrectly\" % (text))\n\n\n    def assertParsesToFLReferenceChildren(self, text, children):\n        prefix = ''.join(takewhile(lambda x: x in ' =', text))\n\n        self.assertParsesToTree(text, FLRoot([prefix, TestFromFlReferenceChildren(children)]))\n\n\n    def assertParsesToFLReferenceChild(self, text, child):\n        self.assertParsesToFLReferenceChildren(text, [child])\n\n\n    def assertParsesCorrectly(self, text, constructExpectedTree):\n        self.assertParsesToTree(text, constructExpectedTree(text), )\n\n\n    def assertCannotParse(self, text, errorPosition, errorToken=\"\"):\n        try:\n            parse(text)\n            self.fail(\"No error for invalid text >>>%s<<<\" % (text))\n        except FormulaError, e:\n            if errorPosition == -1:\n                self.assertEquals(str(e),\n                                  \"Possibly incomplete formula\",\n                                  \"Unexpected error message\")\n            else:\n                self.assertEquals(str(e),\n                                  \"Error in formula at position %d: unexpected '%s'\" % (errorPosition, errorToken),\n                                  \"Unexpected error message\")\n\n\n    def testOne(self):\n        def CreateParseTree(equals, number):\n            return FLRoot([equals, Test_AndTest_NotTest_Comparison(\n                ExprFromAtomChild(Number([number])))])\n\n        self.assertParsesToTree(\"=1\", CreateParseTree(\"=\", \"1\"))\n        self.assertParsesToTree(\"=  1\", CreateParseTree(\"=  \", \"1\"))\n        self.assertCannotParse(\" = 1\", 1, \" = 1\")\n\n\n    def testFLD(self):\n        \"test f l d d e call\"\n        self.assertParsesToFLReferenceChild(\n            \"='hello'!'goodbye'\", FLDDECall([\"'hello'\", \"!\", \"'goodbye'\"]))\n        self.assertParsesToFLReferenceChild(\n            \"='BLP|M'!'GBPEUR Curncy,[PX_MID]'\", FLDDECall([\"'BLP|M'\", \"!\", \"'GBPEUR Curncy,[PX_MID]'\"]))\n\n\n    def testFLNaked(self):\n        \"test f l naked worksheet reference\"\n        self.assertParsesToFLReferenceChild(\n            \"=<Sheet1>\", FLNakedWorksheetReference([\"<\", \"Sheet1\", \">\"]))\n        self.assertParsesToFLReferenceChild(\n            \"=< Sheet1 > \", FLNakedWorksheetReference([\"< \", \"Sheet1 \", \"> \"]))\n        self.assertParsesToFLReferenceChild(\n            \"=<'Sheet1'>\", FLNakedWorksheetReference([\"<\", \"'Sheet1'\", \">\"]))\n        self.assertParsesToFLReferenceChild(\n            \"=<'didn''t'>\", FLNakedWorksheetReference([\"<\", \"'didn''t'\", \">\"]))\n\n\n    def testFLNaked2(self):\n        \"test f l naked worksheet reference with trailers\"\n        self.assertParsesToFLReferenceChildren(\n            \"=<Sheet1>.Bounds\", [FLNakedWorksheetReference([\"<\", \"Sheet1\", \">\"]), Trailer([\".\", Name([\"Bounds\"])])])\n\n\n\n    def testFLNaked3(self):\n        \"test f l naked worksheet reference becomes normal cell reference where appropriate\"\n        self.assertParsesToFLReferenceChild(\n            \"=<Worksheet>!A1\", FLCellReference([\"Worksheet\", \"!\", \"A1\"]))\n\n        self.assertParsesToFLReferenceChild(\n            \"=<'Worksheet'>!A1\", FLCellReference([\"'Worksheet'\", \"!\", \"A1\"]))\n\n        self.assertParsesToFLReferenceChild(\n            \"=<'I didn''t do it!'>  !A1\", FLCellReference([\"'I didn''t do it!'  \", \"!\", \"A1\"]))\n\n        self.assertParsesToFLReferenceChild(\n            \"=<Sheet1>!#Invalid!\", FLInvalidReference([\"Sheet1\", \"!\", \"#Invalid!\"]))\n\n        self.assertParsesToFLReferenceChild(\n            \"=<Sheet1>!#Deleted!\", FLDeletedReference([\"Sheet1\", \"!\", \"#Deleted!\"]))\n\n        self.assertCannotParse(\"=< 'boo!'>\", 4, \"'boo!'\")\n        self.assertCannotParse(\"=<'boo!' >\", 3, \"'boo!' \")\n\n\n    def testWorksheetExpressions(self):\n        def CreateParseTree(children):\n            return FLRoot([\"=\", Test_AndTest_NotTest_Comparison(\n                Expr_ConcatExpr_ShiftExpr(children))])\n\n        self.assertParsesToTree(\"=<Sheet1>+<Sheet2>\", CreateParseTree(\n            ArithExpr(\n                [Term(\n                    [Factor(\n                        [Power(\n                            [FLReference([FLNakedWorksheetReference([\"<\", \"Sheet1\", \">\"])])])])]),\n                \"+\",\n                Term(\n                    [Factor(\n                        [Power(\n                            [FLReference([FLNakedWorksheetReference([\"<\", \"Sheet2\", \">\"])])])])])]\n                )))\n\n\n    def testFLInvalid(self):\n        \"test f l invalid reference\"\n        self.assertParsesToFLReferenceChild(\n            \"=#Invalid!\", FLInvalidReference([\"#Invalid!\"]))\n\n        self.assertParsesToFLReferenceChild(\n            \"=Sheet1!#Invalid!\", FLInvalidReference([\"Sheet1\", \"!\", \"#Invalid!\"]))\n\n        self.assertParsesToFLReferenceChild(\n            \"=A_!#Invalid!\", FLInvalidReference([\"A_\", \"!\", \"#Invalid!\"]))\n\n        self.assertParsesToFLReferenceChild(\n            \"=_1!#Invalid!\", FLInvalidReference([\"_1\", \"!\", \"#Invalid!\"]))\n\n        self.assertParsesToFLReferenceChild(\n            \"=A1!#Invalid!\", FLInvalidReference([\"A1\", \"!\", \"#Invalid!\"]))\n\n\n    def testFLDeleted(self):\n        \"test f l deleted reference\"\n        self.assertParsesToFLReferenceChild(\n            \"=#Deleted!\", FLDeletedReference([\"#Deleted!\"]))\n\n        self.assertParsesToFLReferenceChild(\n            \"=Sheet1!#Deleted!\", FLDeletedReference([\"Sheet1\", \"!\", \"#Deleted!\"]))\n\n        self.assertParsesToFLReferenceChild(\n            \"='Sheet1!'!#Deleted!\", FLDeletedReference([\"'Sheet1!'\", \"!\", \"#Deleted!\"]))\n\n        self.assertParsesToFLReferenceChild(\n            \"=A_!#Deleted!\", FLDeletedReference([\"A_\", \"!\", \"#Deleted!\"]))\n\n        self.assertParsesToFLReferenceChild(\n            \"=_1!#Deleted!\", FLDeletedReference([\"_1\", \"!\", \"#Deleted!\"]))\n\n        self.assertParsesToFLReferenceChild(\n            \"=A1!#Deleted!\", FLDeletedReference([\"A1\", \"!\", \"#Deleted!\"]))\n\n\n    def testFLReference(self):\n        \"test f l reference nasty cases\"\n        self.assertParsesToTree(\"=#Deleted!+B26\",\n            FLRoot([\"=\", Test_AndTest_NotTest_Comparison(\n                Expr_ConcatExpr_ShiftExpr(\n                    ArithExpr([\n                        Term([Factor([Power([FLReference([\n                            FLDeletedReference([\"#Deleted!\"])])])])]),\n                        \"+\",\n                        Term([Factor([Power([FLReference([\n                            FLCellReference([\"B26\"])])])])])\n                        ])))]))\n\n        self.assertParsesToTree(\"='x'!='y'\",\n            FLRoot([\"=\", Test([AndTest([NotTest([Comparison(\n                [\n                Expr_ConcatExpr_ShiftExpr(\n                    ArithExpr_Term(Factor_Power_FLReference_Atom(StringLiteral([\"'x'\"])))),\n                CompOperator([\"!=\"]),\n                Expr_ConcatExpr_ShiftExpr(\n                    ArithExpr_Term(Factor_Power_FLReference_Atom(StringLiteral([\"'y'\"]))))\n                ])])])])]))\n\n\n\n    def testFLSum(self):\n        def CreateParseTree(name, lBracket, argList, rBracket):\n            return FLRoot([\"=\", CreateTest(name, lBracket, argList, rBracket)])\n\n        self.assertParsesToTree(\"=SUM(A1:B2)\", CreateParseTree(\n            \"SUM\",\n            \"(\",\n            [\n                ArgumentFromFLReferenceChild(FLCellRange(\n                    [\n                    FLCellReference([\"A1\"]),\n                    \":\",\n                    FLCellReference([\"B2\"])\n                    ]))\n            ],\n            \")\"\n            ))\n        self.assertParsesToTree(\"=sum(A1:B2)\", CreateParseTree(\n            \"sum\",\n            \"(\",\n            [ArgumentFromFLReferenceChild(FLCellRange(\n                [\n                FLCellReference([\"A1\"]),\n                \":\",\n                FLCellReference([\"B2\"])\n                ]))\n            ],\n            \")\"\n            ))\n        self.assertParsesToTree(\"=suM(A1:B2)\", CreateParseTree(\n            \"suM\",\n            \"(\",\n             [\n                ArgumentFromFLReferenceChild(FLCellRange(\n                    [\n                    FLCellReference([\"A1\"]),\n                    \":\",\n                    FLCellReference([\"B2\"])\n                ]))\n             ],\n            \")\"\n            ))\n\n        self.assertParsesToTree(\"=SUM ( A1 : B2 ) \", CreateParseTree(\n            \"SUM \",\n            \"( \",\n            [\n                ArgumentFromFLReferenceChild(FLCellRange(\n                    [\n                    FLCellReference([\"A1 \"]),\n                    \": \",\n                    FLCellReference([\"B2 \"])\n                    ]))\n            ],\n            \") \"\n            ))\n\n        self.assertParsesToTree(\"=SUM(A1:#Deleted!)\", CreateParseTree(\n            \"SUM\",\n            \"(\",\n            [\n                ArgumentFromFLReferenceChild(FLCellRange(\n                    [\n                    FLCellReference([\"A1\"]),\n                    \":\",\n                    FLDeletedReference([\"#Deleted!\"]),\n                    ]))\n            ],\n            \")\"\n            ))\n\n        self.assertParsesToTree(\"=SUM(#Deleted!:B2)\", CreateParseTree(\n            \"SUM\",\n            \"(\",\n            [\n                ArgumentFromFLReferenceChild(FLCellRange(\n                    [\n                    FLDeletedReference([\"#Deleted!\"]),\n                    \":\",\n                    FLCellReference([\"B2\"]),\n                    ]))\n            ],\n            \")\"\n            ))\n\n        self.assertParsesToTree(\"=SUM(#Deleted!:#Deleted!)\", CreateParseTree(\n            \"SUM\",\n            \"(\",\n            [\n                ArgumentFromFLReferenceChild(FLCellRange(\n                    [\n                    FLDeletedReference([\"#Deleted!\"]),\n                    \":\",\n                    FLDeletedReference([\"#Deleted!\"]),\n                    ]))\n            ],\n            \")\"\n            ))\n\n        self.assertParsesToTree(\"=SUM(A1:#Invalid!)\", CreateParseTree(\n            \"SUM\",\n            \"(\",\n            [\n                ArgumentFromFLReferenceChild(FLCellRange(\n                    [\n                    FLCellReference([\"A1\"]),\n                    \":\",\n                    FLInvalidReference([\"#Invalid!\"]),\n                    ]))\n            ],\n            \")\"\n            ))\n\n        self.assertParsesToTree(\"=SUM(#Invalid!:B2)\", CreateParseTree(\n            \"SUM\",\n            \"(\",\n            [\n                ArgumentFromFLReferenceChild(FLCellRange(\n                    [\n                    FLInvalidReference([\"#Invalid!\"]),\n                    \":\",\n                    FLCellReference([\"B2\"]),\n                    ]))\n            ],\n            \")\"\n            ))\n\n        self.assertParsesToTree(\"=SUM(#Invalid!:#Invalid!)\", CreateParseTree(\n            \"SUM\",\n            \"(\",\n            [\n                ArgumentFromFLReferenceChild(FLCellRange(\n                    [\n                    FLInvalidReference([\"#Invalid!\"]),\n                    \":\",\n                    FLInvalidReference([\"#Invalid!\"]),\n                    ]))\n            ],\n            \")\"\n            ))\n\n        self.assertCannotParse(\"=SUM(A1:AAAA1)\", 8, \"AAAA1\")\n        self.assertCannotParse(\"=SUM(AAAA1:A1)\", 5, \"AAAA1\")\n\n        self.assertCannotParse(\"=SUM(A1:FXSHRXX1)\", 8, \"FXSHRXX1\")\n        self.assertCannotParse(\"=SUM(FXSHRXX1:A1)\", 5, \"FXSHRXX1\")\n\n        self.assertParsesToTree(\"=SUM(A1:B2,A2:B1)\", CreateParseTree(\n            \"SUM\",\n            \"(\",\n                [\n                    ArgumentFromFLReferenceChild(FLCellRange(\n                        [\n                        FLCellReference([\"A1\"]),\n                        \":\",\n                        FLCellReference([\"B2\"])\n                    ])),\n                    \",\",\n                    ArgumentFromFLReferenceChild(FLCellRange(\n                        [\n                        FLCellReference([\"A2\"]),\n                        \":\",\n                        FLCellReference([\"B1\"])\n                    ]))\n                ],\n            \")\"\n            ))\n\n\n        self.assertParsesToTree(\"=SUM(A1:B2,B1)\", CreateParseTree(\n            \"SUM\",\n            \"(\",\n             [\n                    ArgumentFromFLReferenceChild(FLCellRange(\n                        [\n                        FLCellReference([\"A1\"]),\n                        \":\",\n                        FLCellReference([\"B2\"])\n                    ])),\n                    \",\",\n                    ArgumentFromFLReferenceChild(FLCellReference([\"B1\"]))\n             ],\n            \")\"\n            ))\n\n        self.assertParsesToTree(\"=SUM(A1:B2,SUM(A1))\", CreateParseTree(\n            \"SUM\",\n            \"(\",\n             [\n                    ArgumentFromFLReferenceChild(FLCellRange(\n                        [\n                        FLCellReference([\"A1\"]),\n                        \":\",\n                        FLCellReference([\"B2\"])\n                    ])),\n                    \",\",\n                    CreateTest('SUM', '(', [ArgumentFromFLReferenceChild(FLCellReference([\"A1\"]))], ')')\n             ],\n            \")\"\n            ))\n\n\n        self.assertParsesToTree(\"=SUM(A1,A2,A3)\", CreateParseTree(\n            \"SUM\",\n            \"(\",\n            [\n                ArgumentFromFLReferenceChild(FLCellReference([\"A1\"])),\n                \",\",\n                ArgumentFromFLReferenceChild(FLCellReference([\"A2\"])),\n                \",\",\n                ArgumentFromFLReferenceChild(FLCellReference([\"A3\"]))\n            ],\n            \")\"))\n\n        self.assertParsesToTree(\"=SUM(<Sheet1>)\", CreateParseTree(\n            \"SUM\",\n            \"(\",\n            [\n                FLNakedWorksheetReference([\"<\", \"Sheet1\", \">\"])\n            ],\n            \")\"\n            ))\n\n        self.assertParsesToTree(\"=SUM(<Sheet1>,<Sheet2>)\", CreateParseTree(\n            \"SUM\",\n            \"(\",\n            [\n                FLNakedWorksheetReference([\"<\", \"Sheet1\", \">\"]),\n                \",\",\n                FLNakedWorksheetReference([\"<\", \"Sheet2\", \">\"])\n            ],\n            \")\"\n            ))\n        self.assertParsesToTree(\"=SUM(Sheet1!A1:Sheet1!B1)\", CreateParseTree(\n            \"SUM\",\n            \"(\",\n            [\n                 ArgumentFromFLReferenceChild(FLCellRange([\n                        FLCellReference([\"Sheet1\", \"!\", \"A1\"]),\n                        \":\",\n                        FLCellReference([\"Sheet1\", \"!\", \"B1\"])\n                ]))\n            ],\n            \")\"\n            ))\n\n        self.assertParsesToTree(\"=SUM(A1,1/7)\", CreateParseTree(\n             \"SUM\",\n             \"(\",\n             [\n                FLCellReference([\"A1\"]),\n                ',',\n                Test_AndTest_NotTest_Comparison(\n                                Expr_ConcatExpr_ShiftExpr(\n                    ArithExpr(\n                            [Term(\n                                [\n                                Factor_Power_FLReference_Atom(Number([\"1\"])),\n                                \"/\",\n                                Factor_Power_FLReference_Atom(Number([\"7\"]))\n                                ])])))\n             ],\n             \")\"\n             ))\n\n\n    def testFLCell(self):\n        \"test f l cell range\"\n        def CreateParseTree(name, lBracket, argList, rBracket):\n            return FLRoot([\"=\", CreateTest(name, lBracket, argList, rBracket)])\n        self.assertParsesToTree(\"=SUM(Sheet1!A1:B2)\", CreateParseTree(\n            \"SUM\",\n            \"(\",\n                [ArgumentFromFLReferenceChild(\n                    FLCellRange(\n                        [\n                        FLCellReference([\"Sheet1\", \"!\", \"A1\"]),\n                        \":\",\n                        FLCellReference([\"Sheet1\", \"!\", \"B2\"])\n                    ]),\n                )],\n            \")\"\n            ))\n\n        self.assertParsesToTree(\"=SUM(Sheet1!A1:Sheet2!B2)\", CreateParseTree(\n            \"SUM\",\n            \"(\",\n                [ArgumentFromFLReferenceChild(\n                    FLCellRange(\n                        [\n                        FLCellReference([\"Sheet1\", \"!\", \"A1\"]),\n                        \":\",\n                        FLCellReference([\"Sheet2\", \"!\", \"B2\"])\n                    ]),\n                )],\n            \")\"\n            ))\n\n        self.assertParsesToFLReferenceChild(\"= A1:Sheet1!B1\", FLCellRange([\n            FLCellReference([\"A1\"]), \":\", FLCellReference([\"Sheet1\", \"!\", \"B1\"])]))\n\n        self.assertParsesToFLReferenceChild(\"= Sheet1!A1:B1\", FLCellRange([\n            FLCellReference([\"Sheet1\", \"!\", \"A1\"]), \":\", FLCellReference([\"Sheet1\", \"!\", \"B1\"])]))\n\n        self.assertParsesToFLReferenceChild(\"= Sheet1!A1:Sheet1!B1\", FLCellRange([\n            FLCellReference([\"Sheet1\", \"!\", \"A1\"]), \":\", FLCellReference([\"Sheet1\", \"!\", \"B1\"])]))\n\n        self.assertParsesToFLReferenceChild(\"= A1:B1\", FLCellRange([\n            FLCellReference([\"A1\"]), \":\", FLCellReference([\"B1\"])]))\n\n\n    def testCellRangeWith(self):\n        \"test cell range with dictionaries\"\n        def CreateParseTree(lBrace, key, littleArrow, value, rBrace):\n            return FLRoot([\"=\", Test_AndTest_NotTest_Comparison(\n                    Expr_ConcatExpr_ShiftExpr(\n                        ArithExpr_Term(\n                            Factor([Power([FLReference([Atom(\n                                [\n                                lBrace,\n                                DictMaker(\n                                    [\n                                    key,\n                                    littleArrow,\n                                    value\n                                    ]),\n                                rBrace\n                                ])])])]))))])\n\n        self.assertParsesToTree(\"={A1-> A1:B2}\",\n                                CreateParseTree('{',\n                                                TestFromFlReferenceChildren([FLCellReference(['A1'])]),\n                                                '-> ',\n                                                TestFromFlReferenceChildren([\n                                                    FLCellRange([\n                                                        FLCellReference([\"A1\"]),\n                                                        \":\",\n                                                        FLCellReference([\"B2\"])\n                                                    ])\n                                                ]),\n                                                '}'))\n\n        self.assertParsesToTree(\"={A11:A1->B2}\",\n                                CreateParseTree('{',\n                                                TestFromFlReferenceChildren([\n                                                    FLCellRange([\n                                                        FLCellReference([\"A11\"]),\n                                                        \":\",\n                                                        FLCellReference([\"A1\"])\n                                                    ])\n                                                ]),\n                                                '->',\n                                                TestFromFlReferenceChildren([FLCellReference(['B2'])]),\n                                                '}'))\n\n        self.assertParsesToTree(\"={B2:C3 -> A1:B2}\",\n                                CreateParseTree('{',\n                                                TestFromFlReferenceChildren([\n                                                    FLCellRange([\n                                                        FLCellReference([\"B2\"]),\n                                                        \":\",\n                                                        FLCellReference([\"C3 \"])\n                                                    ])\n                                                ]),\n                                                '-> ',\n                                                TestFromFlReferenceChildren([\n                                                    FLCellRange([\n                                                        FLCellReference([\"A1\"]),\n                                                        \":\",\n                                                        FLCellReference([\"B2\"])\n                                                    ])\n                                                ]),\n                                                '}'))\n\n        self.assertParsesToTree(\"={A-> A1:B2}\",\n                                CreateParseTree('{',\n                                                Test_AndTest_NotTest_Comparison(\n                                                    ExprFromNameChild(\"A\")),\n                                                '-> ',\n                                                TestFromFlReferenceChildren([\n                                                    FLCellRange([\n                                                        FLCellReference([\"A1\"]),\n                                                        \":\",\n                                                        FLCellReference([\"B2\"])\n                                                    ])\n                                                ]),\n                                                '}'))\n\n\n    def testCellRangeWith2(self):\n        \"test cell range with lambdas\"\n        def CreateFLRoot(test):\n            return FLRoot([\"=\", test])\n        def CreateLambda(lambdastr, params, littleArrow, bodyTerm):\n            return Test([LambDef(\n                             [\n                             lambdastr,\n                             params,\n                             littleArrow,\n                             bodyTerm\n                        ])\n                    ])\n        def CreateDictionary(lBrace, key, littleArrow, value, rBrace):\n            return Test_AndTest_NotTest_Comparison(\n                    Expr_ConcatExpr_ShiftExpr(\n                        ArithExpr_Term(\n                            Factor([Power([FLReference([Atom(\n                                [\n                                lBrace,\n                                DictMaker(\n                                    [\n                                    key,\n                                    littleArrow,\n                                    value\n                                    ]),\n                                rBrace\n                                ])])])]))))\n\n        self.assertCannotParse(\"= {lambda A1->B2:C3}\", 11, 'A1')\n        self.assertCannotParse(\"= {lambda A1:B2->C3}\", 11, 'A1')\n        self.assertCannotParse(\"= {lambda A1:B2->C3:D4}\", 11, 'A1')\n\n        self.assertParsesToTree(\"=lambda A-> A1:B2\",\n                                CreateFLRoot(\n                                    CreateLambda(\"lambda \",\n                                        VarArgsList([FPDef([Name([\"A\"])])]),\n                                        \"-> \",\n                                        TestFromFlReferenceChildren([\n                                                    FLCellRange([\n                                                        FLCellReference([\"A1\"]),\n                                                        \":\",\n                                                        FLCellReference([\"B2\"])\n                                                    ])\n                                        ])\n                                    )\n                                )\n                               )\n\n        self.assertParsesToTree(\"=lambda x:=A1->B2: C3\",\n                                CreateFLRoot(\n                                    CreateLambda(\"lambda \",\n                                        VarArgsList(\n                                            [\n                                            FPDef([Name([\"x\"])]),\n                                            \":=\",\n                                            TestFromFlReferenceChildren([FLCellReference(['A1'])])\n                                        ]),\n                                        \"->\",\n                                        TestFromFlReferenceChildren([\n                                                    FLCellRange([\n                                                        FLCellReference([\"B2\"]),\n                                                        \": \",\n                                                        FLCellReference([\"C3\"])\n                                                    ])\n                                        ])\n                                    )\n                                )\n                               )\n\n        self.assertParsesToTree(\"={lambda A->B2->C3:D4}\",\n                                CreateFLRoot(\n                                    CreateDictionary(\n                                        \"{\",\n                                        CreateLambda(\"lambda \",\n                                            VarArgsList([FPDef([Name([\"A\"])])]),\n                                            \"->\",\n                                            TestFromFlReferenceChildren([FLCellReference([\"B2\"])])\n                                        ),\n                                        '->',\n                                        TestFromFlReferenceChildren([\n                                            FLCellRange([\n                                                FLCellReference([\"C3\"]),\n                                                \":\",\n                                                FLCellReference([\"D4\"])\n                                            ])\n                                        ]),\n                                        \"}\"\n                                    )\n                                )\n                               )\n\n        self.assertParsesToTree(\"={lambda A->B2:C3->D4}\",\n                                CreateFLRoot(\n                                    CreateDictionary(\n                                        \"{\",\n                                        CreateLambda(\"lambda \",\n                                            VarArgsList([FPDef([Name([\"A\"])])]),\n                                            \"->\",\n                                            TestFromFlReferenceChildren([\n                                                FLCellRange([\n                                                    FLCellReference([\"B2\"]),\n                                                    \":\",\n                                                    FLCellReference([\"C3\"])\n                                                ])\n                                            ])\n                                        ),\n                                        '->',\n                                        TestFromFlReferenceChildren([FLCellReference([\"D4\"])]),\n                                        \"}\"\n                                    )\n                                )\n                               )\n\n\n    def testCellRangeWith3(self):\n        \"test cell range with subscription\"\n        def CreateParseTree(obj, lBracket, subscripts, rBracket):\n            return FLRoot([\"=\", Test_AndTest_NotTest_Comparison(\n                Expr_ConcatExpr_ShiftExpr(\n                        ArithExpr_Term(\n                            Factor(\n                                [Power(\n                                    [FLReference(\n                                        [\n                                        Atom([obj]),\n                                        Trailer(\n                                            [\n                                            lBracket,\n                                            SubscriptList(subscripts),\n                                            rBracket\n                                            ])\n                                        ])])]))))])\n\n        self.assertParsesToTree(\"=a [A1:B2]\",\n            CreateParseTree(\n                Name([\"a \"]),\n                \"[\",\n                [Subscript([\n                    TestFromFlReferenceChildren([\n                        FLCellRange([\n                            FLCellReference([\"A1\"]),\n                            \":\",\n                            FLCellReference([\"B2\"])\n                        ])\n                    ])\n                ])],\n                \"]\"))\n\n        self.assertCannotParse(\"= a[A1:B2:C3]\", 10, ':')\n        self.assertCannotParse(\"= a[A1:B2:C3:D4]\", 10, ':')\n\n    def testCallWithGen(self):\n        \"test call with gen expr\"\n        def CreateParseTree(atom, lBracket, argument, rBracket):\n            return FLRoot([\"=\", Test_AndTest_NotTest_Comparison(\n                    Expr_ConcatExpr_ShiftExpr(\n                        ArithExpr_Term(\n                            Factor(\n                                [Power(\n                                    [FLReference(\n                                        [\n                                        atom,\n                                        Trailer(\n                                            [\n                                            lBracket,\n                                            ArgList([argument]),\n                                            rBracket\n                                            ])\n                                        ])])]))))])\n\n        self.assertParsesToTree(\n            \"=a(x for x in L)\",\n            CreateParseTree(\n                Atom([Name([\"a\"])]),\n                \"(\",\n                Argument(\n                    [\n                    TestFromAtomChild(Name([\"x \"])),\n                    GenFor(\n                        [\n                        \"for \",\n                        ExprList(\n                            [ExprFromAtomChild(Name([\"x \"]))]),\n                        \"in \",\n                        TestFromAtomChild(Name([\"L\"]))\n                        ])\n                    ]),\n                \")\"\n                ))\n\n        # Although the 2.4.3 grammar allows the following form, it shouldn't.\n        # See http://mail.python.org/pipermail/python-dev/2006-February/061544.html\n        self.assertCannotParse(\"=a(x = 5 (for x in L))\", 11, \"for \")\n\n\n    def testPower(self):\n        def CreateParseTree(flReferenceChildren, operator, exponent):\n            return FLRoot([\"=\", Test_AndTest_NotTest_Comparison(\n                Expr_ConcatExpr_ShiftExpr(\n                        ArithExpr_Term(\n                            Factor(\n                                [Power(\n                                    [\n                                    FLReference(flReferenceChildren),\n                                    operator,\n                                    exponent\n                                    ])]))))])\n\n        self.assertParsesToTree(\"=a**b\", CreateParseTree([Atom([Name([\"a\"])])], \"**\", Factor_Power_FLReference_Atom(Name([\"b\"]))))\n        self.assertParsesToTree(\"=a^b\", CreateParseTree([Atom([Name([\"a\"])])], \"^\", Factor_Power_FLReference_Atom(Name([\"b\"]))))\n        self.assertParsesToTree(\"=a0**b2\", CreateParseTree([Atom([Name([\"a0\"])])], \"**\", Factor([Power([FLReference([FLCellReference([\"b2\"])])])])))\n        self.assertParsesToTree(\"=a0^b2\", CreateParseTree([Atom([Name([\"a0\"])])], \"^\", Factor([Power([FLReference([FLCellReference([\"b2\"])])])])))\n        self.assertParsesToTree(\"=e2**e0\", CreateParseTree([FLCellReference([\"e2\"])], \"**\", Factor_Power_FLReference_Atom(Name([\"e0\"]))))\n        self.assertParsesToTree(\"=e2^e0\", CreateParseTree([FLCellReference([\"e2\"])], \"^\", Factor_Power_FLReference_Atom(Name([\"e0\"]))))\n        self.assertParsesToTree(\"=j0**j\", CreateParseTree([Atom([Name([\"j0\"])])], \"**\", Factor_Power_FLReference_Atom(Name([\"j\"]))))\n        self.assertParsesToTree(\"=j0^j\", CreateParseTree([Atom([Name([\"j0\"])])], \"^\", Factor_Power_FLReference_Atom(Name([\"j\"]))))\n\n        self.assertParsesToTree(\"=j0.x**j\",\n            CreateParseTree(\n                [\n                Atom([Name([\"j0\"])]),\n                Trailer([\".\", Name([\"x\"])])\n                ],\n                \"**\",\n                Factor_Power_FLReference_Atom(Name([\"j\"]))))\n\n        self.assertParsesToTree(\"=j0.x^j\",\n            CreateParseTree(\n                [\n                Atom([Name([\"j0\"])]),\n                Trailer([\".\", Name([\"x\"])])\n                ],\n                \"^\",\n                Factor_Power_FLReference_Atom(Name([\"j\"]))))\n\n        self.assertParsesToTree(\"=j0.x(15)[p->q]**j\",\n            CreateParseTree(\n                [\n                Atom([Name([\"j0\"])]),\n                Trailer([\".\", Name([\"x\"])]),\n                Trailer(\n                    [\n                    \"(\",\n                    ArgList([Argument([TestFromAtomChild(Number([\"15\"]))])]),\n                    \")\"\n                    ]),\n                Trailer(\n                    [\n                    \"[\",\n                    SubscriptList([Subscript(\n                        [\n                        TestFromAtomChild(Name([\"p\"])),\n                        \"->\",\n                        TestFromAtomChild(Name([\"q\"]))\n                        ])]),\n                    \"]\"\n                    ]),\n                ],\n                \"**\",\n                Factor_Power_FLReference_Atom(Name([\"j\"]))))\n\n        self.assertParsesToTree(\"=j0.x(15)[p->q]^j\",\n            CreateParseTree(\n                [\n                Atom([Name([\"j0\"])]),\n                Trailer([\".\", Name([\"x\"])]),\n                Trailer(\n                    [\n                    \"(\",\n                    ArgList([Argument([TestFromAtomChild(Number([\"15\"]))])]),\n                    \")\"\n                    ]),\n                Trailer(\n                    [\n                    \"[\",\n                    SubscriptList([Subscript(\n                        [\n                        TestFromAtomChild(Name([\"p\"])),\n                        \"->\",\n                        TestFromAtomChild(Name([\"q\"]))\n                        ])]),\n                    \"]\"\n                    ]),\n                ],\n                \"^\",\n                Factor_Power_FLReference_Atom(Name([\"j\"]))))\n\n\n    def testNestedFactor(self):\n        def CreateParseTree(unaryOperation, operand):\n            return FLRoot([\"=\", Test_AndTest_NotTest_Comparison(\n                Expr_ConcatExpr_ShiftExpr(\n                        ArithExpr_Term(\n                            Factor(\n                                [\n                                unaryOperation,\n                                Factor_Power_FLReference_Atom(Number([operand]))\n                                ]))))])\n\n        self.assertParsesToTree(\"=+1\", CreateParseTree(\"+\", \"1\"))\n        self.assertParsesToTree(\"=-1\", CreateParseTree(\"-\", \"1\"))\n        self.assertParsesToTree(\"=~1\", CreateParseTree(\"~\", \"1\"))\n\n\n    def testPercent(self):\n        def CreateParseTree(operand, percentOperator):\n            return FLRoot([\"=\", Test_AndTest_NotTest_Comparison(\n                Expr_ConcatExpr_ShiftExpr(\n                        ArithExpr_Term(\n                            Percent(\n                                [\n                                Factor_Power_FLReference_Atom(operand),\n                                percentOperator\n                                ]))))])\n\n        self.assertParsesToTree(\"=10%\", CreateParseTree(Number([\"10\"]), \"%\"))\n        self.assertParsesToTree(\"=10 %\", CreateParseTree(Number([\"10 \"]), \"%\"))\n        self.assertParsesToTree(\"=10% \", CreateParseTree(Number([\"10\"]), \"% \"))\n        self.assertParsesToTree(\"=a % \", CreateParseTree(Name([\"a \"]), \"% \"))\n\n\n    def testFactorFactorTerm(self):\n        def CreateParseTree(moperator, uoperator1, operand1, uoperator2, operand2):\n            return FLRoot([\"=\", Test_AndTest_NotTest_Comparison(\n                Expr_ConcatExpr_ShiftExpr(\n                        ArithExpr(\n                            [Term(\n                                [\n                                Factor(\n                                    [uoperator1, Factor_Power_FLReference_Atom(operand1)]),\n                                moperator,\n                                Factor(\n                                    [uoperator2, Factor_Power_FLReference_Atom(operand2)])\n                                ])])))])\n\n        self.assertParsesToTree(\"=-6/-4\", CreateParseTree(\"/\",\n                                                         \"-\", Number([\"6\"]),\n                                                         \"-\", Number([\"4\"])))\n        self.assertParsesToTree(\"=- 6 / - 4\", CreateParseTree(\"/ \",\n                                                             \"- \", Number([\"6 \"]),\n                                                             \"- \", Number([\"4\"])))\n        self.assertParsesToTree(\"=-6/~.4\", CreateParseTree(\"/\",\n                                                         \"-\", Number([\"6\"]),\n                                                         \"~\", Number([\".4\"])))\n        self.assertParsesToTree(\"=- 6 / ~ .4\", CreateParseTree(\"/ \",\n                                                              \"- \", Number([\"6 \"]),\n                                                              \"~ \", Number([\".4\"])))\n        self.assertParsesToTree(\"=-4.5j/+2.4e6\", CreateParseTree(\"/\",\n                                                                \"-\", Number([\"4.5j\"]),\n                                                                \"+\", Number([\"2.4e6\"])))\n        self.assertParsesToTree(\"=- 4.5j / + 2.4e6\", CreateParseTree(\"/ \",\n                                                                    \"- \", Number([\"4.5j \"]),\n                                                                    \"+ \", Number([\"2.4e6\"])))\n\n\n    def testAtomTerm(self):\n        def CreateParseTree(operator, operand1, operand2):\n            return FLRoot([\"=\", Test_AndTest_NotTest_Comparison(\n                Expr_ConcatExpr_ShiftExpr(\n                        ArithExpr(\n                            [Term(\n                                [\n                                Factor_Power_FLReference_Atom(operand1),\n                                operator,\n                                Factor_Power_FLReference_Atom(operand2)\n                                ])])))])\n\n        self.assertParsesToTree(\"=1*2\", CreateParseTree(\"*\", Number([\"1\"]), Number([\"2\"])))\n        self.assertParsesToTree(\"=1 * 2\", CreateParseTree(\"* \", Number([\"1 \"]), Number([\"2\"])))\n        self.assertParsesToTree(\"=1/2\", CreateParseTree(\"/\", Number([\"1\"]), Number([\"2\"])))\n        self.assertParsesToTree(\"=1 / 2\", CreateParseTree(\"/ \", Number([\"1 \"]), Number([\"2\"])))\n        self.assertParsesToTree(\"=1//2\", CreateParseTree(\"//\", Number([\"1\"]), Number([\"2\"])))\n        self.assertParsesToTree(\"=1 // 2\", CreateParseTree(\"// \", Number([\"1 \"]), Number([\"2\"])))\n        self.assertParsesToTree(\"=1%%2\", CreateParseTree(\"%%\", Number([\"1\"]), Number([\"2\"])))\n        self.assertParsesToTree(\"=1 %% 2\", CreateParseTree(\"%% \", Number([\"1 \"]), Number([\"2\"])))\n        self.assertParsesToTree(\"=1.6e38*2\", CreateParseTree(\"*\", Number([\"1.6e38\"]), Number([\"2\"])))\n        self.assertParsesToTree(\"=1.6e38 * 2\", CreateParseTree(\"* \", Number([\"1.6e38 \"]), Number([\"2\"])))\n        self.assertParsesToTree(\"=1./2j\", CreateParseTree(\"/\", Number([\"1.\"]), Number([\"2j\"])))\n        self.assertParsesToTree(\"=1. / 2j\", CreateParseTree(\"/ \", Number([\"1. \"]), Number([\"2j\"])))\n\n\n    def testMultiAtomTerm(self):\n        def CreateParseTree(operand1, operator1, operand2, operator2,  operand3):\n            return FLRoot([\"=\", Test_AndTest_NotTest_Comparison(\n                Expr_ConcatExpr_ShiftExpr(\n                        ArithExpr(\n                            [Term(\n                                [\n                                Factor_Power_FLReference_Atom(operand1),\n                                operator1,\n                                Factor_Power_FLReference_Atom(operand2),\n                                operator2,\n                                Factor_Power_FLReference_Atom(operand3)\n                                ])])))])\n\n        self.assertParsesToTree(\"=1* 2e3/4j\", CreateParseTree(\n            Number([\"1\"]),\n            \"* \",\n            Number([\"2e3\"]),\n            \"/\",\n            Number([\"4j\"])))\n\n\n    def testAtomArithExpr(self):\n        def CreateParseTree(operator, operand1, operand2):\n            return FLRoot([\"=\", Test_AndTest_NotTest_Comparison(\n                Expr_ConcatExpr_ShiftExpr(\n                        ArithExpr(\n                            [\n                            Term(\n                                [Factor_Power_FLReference_Atom(operand1)]),\n                            operator,\n                            Term(\n                                [Factor_Power_FLReference_Atom(operand2)])\n                            ])))])\n\n        self.assertParsesToTree(\"=1+2\", CreateParseTree(\"+\", Number([\"1\"]), Number([\"2\"])))\n        self.assertParsesToTree(\"=1 + 2\", CreateParseTree(\"+ \", Number([\"1 \"]), Number([\"2\"])))\n        self.assertParsesToTree(\"=5e3-2.2\", CreateParseTree(\"-\", Number([\"5e3\"]), Number([\"2.2\"])))\n        self.assertParsesToTree(\"=5e3 - 2.2\", CreateParseTree(\"- \", Number([\"5e3 \"]), Number([\"2.2\"])))\n        self.assertParsesToTree(\"=1j+2e6\", CreateParseTree(\"+\", Number([\"1j\"]), Number([\"2e6\"])))\n        self.assertParsesToTree(\"=1j + 2e6\", CreateParseTree(\"+ \", Number([\"1j \"]), Number([\"2e6\"])))\n\n\n    def testMultiAtomArith(self):\n        \"test multi atom arith expr\"\n        def CreateParseTree(operand1, operator1, operand2, operator2, operand3):\n            return FLRoot([\"=\", Test_AndTest_NotTest_Comparison(\n                Expr_ConcatExpr_ShiftExpr(\n                        ArithExpr(\n                            [\n                            Term(\n                                [Factor_Power_FLReference_Atom(operand1)]),\n                            operator1,\n                            Term(\n                                [Factor_Power_FLReference_Atom(operand2)]),\n                            operator2,\n                            Term(\n                                [Factor_Power_FLReference_Atom(operand3)])\n                            ])))])\n\n        self.assertParsesToTree(\"=1.5+2j  - 5\", CreateParseTree(\n            Number([\"1.5\"]),\n            \"+\",\n            Number([\"2j  \"]),\n            \"- \",\n            Number([\"5\"])))\n\n\n    def testAtomShiftExpr(self):\n        def CreateParseTree(operator, operand1, operand2):\n            return FLRoot([\"=\", Test_AndTest_NotTest_Comparison(\n                    Expr(\n                        [ConcatExpr(\n                            [ShiftExpr(\n                                [\n                                ArithExpr_Term(\n                                    Factor_Power_FLReference_Atom(operand1)),\n                                operator,\n                                ArithExpr_Term(\n                                    Factor_Power_FLReference_Atom(operand2))\n                                ])])]))])\n\n        self.assertParsesToTree(\"=2<<3\", CreateParseTree(\"<<\", Number([\"2\"]), Number([\"3\"])))\n        self.assertParsesToTree(\"=2 << 3\", CreateParseTree(\"<< \", Number([\"2 \"]), Number([\"3\"])))\n        self.assertParsesToTree(\"=2e3>>3J\", CreateParseTree(\">>\", Number([\"2e3\"]), Number([\"3J\"])))\n        self.assertParsesToTree(\"=2e3 >> 3J\", CreateParseTree(\">> \", Number([\"2e3 \"]), Number([\"3J\"])))\n\n\n    def testMultiAtomShift(self):\n        \"test multi atom shift expr\"\n        def CreateParseTree(operand1, operator1, operand2, operator2, operand3):\n            return FLRoot([\"=\", Test_AndTest_NotTest_Comparison(\n                    Expr(\n                        [ConcatExpr(\n                            [ShiftExpr(\n                                [\n                                ArithExpr_Term(\n                                    Factor_Power_FLReference_Atom(operand1)),\n                                operator1,\n                                ArithExpr_Term(\n                                    Factor_Power_FLReference_Atom(operand2)),\n                                operator2,\n                                ArithExpr_Term(\n                                    Factor_Power_FLReference_Atom(operand3))\n                                ])])]))])\n\n        self.assertParsesToTree(\"=2<<3>>7\", CreateParseTree(\n            Number([\"2\"]),\n            \"<<\",\n            Number([\"3\"]),\n            \">>\",\n            Number([\"7\"])))\n\n\n    def testAtomConcatExpr(self):\n        def CreateParseTree(operator, operand1, operand2):\n            return FLRoot([\"=\", Test_AndTest_NotTest_Comparison(\n                Expr(\n                    [ConcatExpr(\n                        [\n                        ShiftExpr(\n                            [ArithExpr_Term(\n                                Factor_Power_FLReference_Atom(operand1))]),\n                        operator,\n                        ShiftExpr(\n                            [ArithExpr_Term(\n                                Factor_Power_FLReference_Atom(operand2))])\n                        ])]))])\n\n        self.assertParsesToTree(\"=a&b\", CreateParseTree(\"&\", Name([\"a\"]), Name([\"b\"])))\n        self.assertParsesToTree(\"=a & b\", CreateParseTree(\"& \", Name([\"a \"]), Name([\"b\"])))\n\n\n    def testMultiAtomConcat(self):\n        \"test multi atom concat expr\"\n        def CreateParseTree(operand1, operator1, operand2, operator2, operand3):\n            return FLRoot([\"=\", Test_AndTest_NotTest_Comparison(\n                Expr(\n                    [ConcatExpr(\n                        [\n                        ShiftExpr(\n                            [ArithExpr_Term(\n                                Factor_Power_FLReference_Atom(operand1))]),\n                        operator1,\n                        ShiftExpr(\n                            [ArithExpr_Term(\n                                Factor_Power_FLReference_Atom(operand2))]),\n                        operator2,\n                        ShiftExpr(\n                            [ArithExpr_Term(\n                                Factor_Power_FLReference_Atom(operand3))])\n                        ])]))])\n\n        self.assertParsesToTree(\"=a&b& c\", CreateParseTree(\n            Name([\"a\"]),\n            \"&\",\n            Name([\"b\"]),\n            \"& \",\n            Name([\"c\"])))\n\n\n    def testExpr(self):\n        def CreateParseTree(operator, operand1, operand2):\n            return FLRoot([\"=\", Test_AndTest_NotTest_Comparison(\n                    Expr(\n                        [\n                        ConcatExpr(\n                            [ShiftExpr(\n                                [ArithExpr_Term(\n                                    Factor_Power_FLReference_Atom(\n                                        Name([operand1])))])]),\n                        operator,\n                        ConcatExpr(\n                            [ShiftExpr(\n                                [ArithExpr_Term(\n                                    Factor_Power_FLReference_Atom(\n                                        Name([operand2])))])])\n                        ]))])\n\n        self.assertParsesToTree(\"=a|b\", CreateParseTree(\"|\", \"a\", \"b\"))\n        self.assertParsesToTree(\"=a | b\", CreateParseTree(\"| \", \"a \", \"b\"))\n\n\n    def testSimpleComparison(self):\n        def CreateParseTree(operand1, operator, operand2):\n            return FLRoot([\"=\", Test(\n                    [AndTest(\n                        [NotTest(\n                            [Comparison(\n                                [\n                                Expr_ConcatExpr_ShiftExpr(\n                                    ArithExpr_Term(\n                                        Factor_Power_FLReference_Atom(operand1)))\n                                ,\n                                CompOperator([operator]),\n                                Expr_ConcatExpr_ShiftExpr(\n                                    ArithExpr_Term(\n                                        Factor_Power_FLReference_Atom(operand2)))\n                                ])])])])])\n\n        self.assertParsesToTree(\"=a<b\", CreateParseTree(Name([\"a\"]), \"<\", Name([\"b\"])))\n        self.assertParsesToTree(\"=a < b\", CreateParseTree(Name([\"a \"]), \"< \", Name([\"b\"])))\n        self.assertParsesToTree(\"=a>b\", CreateParseTree(Name([\"a\"]), \">\", Name([\"b\"])))\n        self.assertParsesToTree(\"=a > b\", CreateParseTree(Name([\"a \"]), \"> \", Name([\"b\"])))\n        self.assertParsesToTree(\"=a==b\", CreateParseTree(Name([\"a\"]), \"==\", Name([\"b\"])))\n        self.assertParsesToTree(\"=a=b\", CreateParseTree(Name([\"a\"]), \"=\", Name([\"b\"])))\n        self.assertParsesToTree(\"=a == b\", CreateParseTree(Name([\"a \"]), \"== \", Name([\"b\"])))\n        self.assertParsesToTree(\"=a = b\", CreateParseTree(Name([\"a \"]), \"= \", Name([\"b\"])))\n        self.assertParsesToTree(\"=a>=b\", CreateParseTree(Name([\"a\"]), \">=\", Name([\"b\"])))\n        self.assertParsesToTree(\"=a >= b\", CreateParseTree(Name([\"a \"]), \">= \", Name([\"b\"])))\n        self.assertParsesToTree(\"=a<=b\", CreateParseTree(Name([\"a\"]), \"<=\", Name([\"b\"])))\n        self.assertParsesToTree(\"=a <= b\", CreateParseTree(Name([\"a \"]), \"<= \", Name([\"b\"])))\n        self.assertParsesToTree(\"=a<>b\", CreateParseTree(Name([\"a\"]), \"<>\", Name([\"b\"])))\n        self.assertParsesToTree(\"=a <> b\", CreateParseTree(Name([\"a \"]), \"<> \", Name([\"b\"])))\n        self.assertParsesToTree(\"=a!=b\", CreateParseTree(Name([\"a\"]), \"!=\", Name([\"b\"])))\n        self.assertParsesToTree(\"=a != b\", CreateParseTree(Name([\"a \"]), \"!= \", Name([\"b\"])))\n\n        self.assertParsesToTree('=\"a\"is\"b\"', CreateParseTree(StringLiteral(['\"a\"']),\n                                                            \"is\",\n                                                            StringLiteral(['\"b\"'])))\n        self.assertParsesToTree('=\"a\" is \"b\"', CreateParseTree(StringLiteral(['\"a\" ']),\n                                                            \"is \",\n                                                            StringLiteral(['\"b\"'])))\n        self.assertParsesToTree('=\"a\"in\"b\"', CreateParseTree(StringLiteral(['\"a\"']),\n                                                            \"in\",\n                                                            StringLiteral(['\"b\"'])))\n        self.assertParsesToTree('=\"a\" in \"b\"', CreateParseTree(StringLiteral(['\"a\" ']),\n                                                            \"in \",\n                                                            StringLiteral(['\"b\"'])))\n        self.assertParsesToTree('=1is 2', CreateParseTree(Number([\"1\"]),\n                                                         \"is \",\n                                                         Number([\"2\"])))\n        self.assertParsesToTree('=1 is 2', CreateParseTree(Number([\"1 \"]),\n                                                          \"is \",\n                                                          Number([\"2\"])))\n        self.assertParsesToTree('=\"a\"is not\"b\"', CreateParseTree(StringLiteral(['\"a\"']),\n                                                                \"is not\",\n                                                                StringLiteral(['\"b\"'])))\n        self.assertParsesToTree('=\"a\"  is   not  \"b\"', CreateParseTree(StringLiteral(['\"a\"  ']),\n                                                                \"is   not  \",\n                                                                StringLiteral(['\"b\"'])))\n        self.assertParsesToTree('=\"a\"not in\"b\"', CreateParseTree(StringLiteral(['\"a\"']),\n                                                                \"not in\",\n                                                                StringLiteral(['\"b\"'])))\n        self.assertParsesToTree('=\"a\" not   in \"b\"', CreateParseTree(StringLiteral(['\"a\" ']),\n                                                                \"not   in \",\n                                                                StringLiteral(['\"b\"'])))\n\n        self.assertCannotParse(\"=1is2\", 3, \"is2\")\n        self.assertCannotParse(\"=1 is2\", 4, \"is2\")\n        self.assertCannotParse(\"=1 isnot 2\", 4, \"isnot \")\n        self.assertCannotParse(\"=x := 2\", 4, \":= \")\n\n\n    def testComplexComparison(self):\n        def CreateParseTree(operand1, operator1, operand2, operator2, operand3):\n            return FLRoot([\"=\", Test(\n                    [AndTest(\n                        [NotTest(\n                            [Comparison(\n                                [\n                                Expr_ConcatExpr_ShiftExpr(\n                                    ArithExpr_Term(\n                                        Factor_Power_FLReference_Atom(operand1)))\n                                ,\n                                CompOperator([operator1])\n                                ,\n                                Expr_ConcatExpr_ShiftExpr(\n                                    ArithExpr_Term(\n                                        Factor_Power_FLReference_Atom(operand2)))\n                                ,\n                                CompOperator([operator2])\n                                ,\n                                Expr_ConcatExpr_ShiftExpr(\n                                    ArithExpr_Term(\n                                        Factor_Power_FLReference_Atom(operand3)))\n                                ])])])])])\n\n\n        self.assertParsesToTree(\"=a is not b is c\", CreateParseTree(Name([\"a \"]),\n                                                                   \"is not \",\n                                                                   Name([\"b \"]),\n                                                                   \"is \",\n                                                                   Name([\"c\"])))\n        self.assertParsesToTree(\"=1 < x <= 4\", CreateParseTree(Number([\"1 \"]),\n                                                              \"< \",\n                                                              Name([\"x \"]),\n                                                              \"<= \",\n                                                              Number([\"4\"])))\n\n\n    def testNotTest(self):\n        def CreateParseTree(operator, operand):\n            return FLRoot([\"=\", Test(\n                    [AndTest(\n                        [NotTest(\n                            [\n                            operator,\n                            NotTest(\n                                [Comparison(\n                                    [Expr_ConcatExpr_ShiftExpr(\n                                        ArithExpr_Term(\n                                            Factor_Power_FLReference_Atom(operand)))])])\n                            ])])])])\n\n        # no need to test trivial case -- is amply tested by almost every other test ;-)\n        self.assertParsesToTree(\"=not 33\", CreateParseTree(\"not \", Number([\"33\"])))\n\n\n    def testNotNotTest(self):\n        def CreateParseTree(operator1, operator2, operand):\n            return FLRoot([\"=\", Test(\n                    [AndTest(\n                        [NotTest(\n                            [\n                            operator1,\n                            NotTest(\n                                [\n                                operator2,\n                                NotTest(\n                                    [Comparison(\n                                        [Expr_ConcatExpr_ShiftExpr(\n                                            ArithExpr_Term(\n                                                Factor_Power_FLReference_Atom(operand)))])])\n                                ])\n                            ]\n                        )])])])\n\n        self.assertParsesToTree(\"=not   not foobar\", CreateParseTree(\"not   \", \"not \", Name([\"foobar\"])))\n\n\n    def testAndTest(self):\n        def CreateParseTree(operand1, operator, operand2):\n            return FLRoot([\"=\", CreateAndTest(operand1, operator, operand2)])\n\n\n        self.assertParsesToTree(\"=22 and 33\", CreateParseTree(Number([\"22 \"]), \"and \", Number([\"33\"])))\n        self.assertParsesToTree(\"=22   and 33\", CreateParseTree(Number([\"22   \"]), \"and \", Number([\"33\"])))\n        self.assertParsesToTree(\"=2and'a'\", CreateParseTree(Number([\"2\"]), \"and\", StringLiteral([\"'a'\"])))\n\n\n    def testMultiAndTest(self):\n        def CreateParseTree(operand1, operator1, operand2, operator2, operand3):\n            return FLRoot([\"=\", Test(\n                    [AndTest(\n                        [\n                        NotTest(\n                            [Comparison(\n                                [Expr_ConcatExpr_ShiftExpr(\n                                    ArithExpr_Term(\n                                        Factor_Power_FLReference_Atom(operand1)))])]),\n                        operator1,\n                        NotTest(\n                            [Comparison(\n                                [Expr_ConcatExpr_ShiftExpr(\n                                    ArithExpr_Term(\n                                        Factor_Power_FLReference_Atom(operand2)))])]),\n                        operator2,\n                        NotTest(\n                            [Comparison(\n                                [Expr_ConcatExpr_ShiftExpr(\n                                    ArithExpr_Term(\n                                        Factor_Power_FLReference_Atom(operand3)))])])\n                        ])])])\n\n\n        self.assertParsesToTree(\"=22 and 33e3 and pi\", CreateParseTree(\n            Number([\"22 \"]),\n            \"and \",\n            Number([\"33e3 \"]),\n            \"and \",\n            Name([\"pi\"])))\n\n\n    def testTest(self):\n        def CreateParseTree(operand1, operator, operand2):\n            return FLRoot([\"=\", Test(\n                    [\n                    AndTest(\n                        [NotTest(\n                            [Comparison(\n                                [Expr_ConcatExpr_ShiftExpr(\n                                    ArithExpr_Term(\n                                        Factor_Power_FLReference_Atom(operand1)))])])]),\n                    operator,\n                    AndTest(\n                        [NotTest(\n                            [Comparison(\n                                [Expr_ConcatExpr_ShiftExpr(\n                                    ArithExpr_Term(\n                                        Factor_Power_FLReference_Atom(operand2)))])])])\n                    ])])\n\n        self.assertParsesToTree(\"=22 or 33\", CreateParseTree(Number([\"22 \"]), \"or \", Number([\"33\"])))\n        self.assertParsesToTree(\"=22   or 33\", CreateParseTree(Number([\"22   \"]), \"or \", Number([\"33\"])))\n        self.assertParsesToTree(\"=2or'a'\", CreateParseTree(Number([\"2\"]), \"or\", StringLiteral([\"'a'\"])))\n\n\n    def testMultiTest(self):\n        def CreateParseTree(operand1, operator1, operand2, operator2, operand3):\n            return FLRoot([\"=\", Test(\n                    [\n                    AndTest(\n                        [NotTest(\n                            [Comparison(\n                                [Expr_ConcatExpr_ShiftExpr(\n                                    ArithExpr_Term(\n                                        Factor_Power_FLReference_Atom(operand1)))])])]),\n                    operator1,\n                    AndTest(\n                        [NotTest(\n                            [Comparison(\n                                [Expr_ConcatExpr_ShiftExpr(\n                                    ArithExpr_Term(\n                                        Factor_Power_FLReference_Atom(operand2)))])])]),\n                    operator2,\n                    AndTest(\n                        [NotTest(\n                            [Comparison(\n                                [Expr_ConcatExpr_ShiftExpr(\n                                    ArithExpr_Term(\n                                        Factor_Power_FLReference_Atom(operand3)))])])])\n                    ])])\n\n        self.assertParsesToTree(\"=22j or 33 or '''CHEESE!'''\", CreateParseTree(\n            Number([\"22j \"]),\n            \"or \",\n            Number([\"33 \"]),\n            \"or \",\n            StringLiteral([\"'''CHEESE!'''\"])))\n\n\n    def testTrivialLambDef(self):\n        def CreateParseTree(lambdastr, littleArrow, atom):\n            return FLRoot([\"=\", Test(\n                [LambDef(\n                    [\n                    lambdastr,\n                    littleArrow,\n                    Test_AndTest_NotTest_Comparison(\n                        Expr_ConcatExpr_ShiftExpr(\n                            ArithExpr_Term(\n                                Factor_Power_FLReference_Atom(atom))))])])])\n\n        self.assertParsesToTree(\"=lambda->1\", CreateParseTree(\"lambda\", \"->\", Number([\"1\"])))\n        self.assertParsesToTree(\"=lambda  ->  x\", CreateParseTree(\"lambda  \", \"->  \", Name([\"x\"])))\n\n        self.assertCannotParse(\"=lambda 3 -> 3\", 9, \"3 \")\n\n\n    def testSimpleLambDef(self):\n        def CreateParseTree(lambdastr, params, littleArrow, bodyTerm):\n            return FLRoot([\"=\", Test(\n                [LambDef(\n                    [\n                    lambdastr,\n                    params,\n                    littleArrow,\n                    Test_AndTest_NotTest_Comparison(\n                            Expr_ConcatExpr_ShiftExpr(\n                                ArithExpr(\n                                    [bodyTerm])))\n                    ])])])\n\n        self.assertParsesToTree(\"=lambda  x -> y\",\n            CreateParseTree(\n                \"lambda  \",\n                 VarArgsList([FPDef([Name([\"x \"])])]),\n                 \"-> \",\n                 Term([Factor_Power_FLReference_Atom(Name([\"y\"]))])))\n\n        self.assertParsesToTree(\"=lambda  x, -> y\",\n            CreateParseTree(\n                \"lambda  \",\n                 VarArgsList([FPDef([Name([\"x\"])]), \", \"]),\n                 \"-> \",\n                 Term([Factor_Power_FLReference_Atom(Name([\"y\"]))])))\n\n        self.assertParsesToTree(\"=lambda  x,y   -> y\",\n            CreateParseTree(\n                \"lambda  \",\n                 VarArgsList(\n                    [\n                    FPDef([Name([\"x\"])]),\n                    \",\",\n                    FPDef([Name([\"y   \"])]),\n                    ]),\n                 \"-> \",\n                 Term([Factor_Power_FLReference_Atom(Name([\"y\"]))])))\n\n        self.assertParsesToTree(\"=lambda  x,y  ,z, -> y\",\n            CreateParseTree(\n                \"lambda  \",\n                 VarArgsList(\n                    [\n                    FPDef([Name([\"x\"])]),\n                    \",\",\n                    FPDef([Name([\"y  \"])]),\n                    \",\",\n                    FPDef([Name([\"z\"])]),\n                    \", \",\n                    ]),\n                 \"-> \",\n                 Term([Factor_Power_FLReference_Atom(Name([\"y\"]))])))\n\n        self.assertParsesToTree(\"=lambda  *x -> y\",\n            CreateParseTree(\n                \"lambda  \",\n                 VarArgsList(\n                    [\n                    \"*\",\n                    Name([\"x \"])\n                    ]),\n                 \"-> \",\n                 Term([Factor_Power_FLReference_Atom(Name([\"y\"]))])))\n\n        self.assertParsesToTree(\"=lambda  **x -> y\",\n            CreateParseTree(\n                \"lambda  \",\n                 VarArgsList(\n                    [\n                    \"**\",\n                    Name([\"x \"])\n                    ]),\n                 \"-> \",\n                 Term([Factor_Power_FLReference_Atom(Name([\"y\"]))])))\n\n        self.assertParsesToTree(\"=lambda  *x, **y -> y\",\n            CreateParseTree(\n                \"lambda  \",\n                 VarArgsList(\n                    [\n                    \"*\",\n                    Name([\"x\"]),\n                    \", \",\n                    \"**\",\n                    Name([\"y \"]),\n                    ]),\n                 \"-> \",\n                 Term([Factor_Power_FLReference_Atom(Name([\"y\"]))])))\n\n        self.assertParsesToTree(\"=lambda  x, *y -> y\",\n            CreateParseTree(\n                \"lambda  \",\n                 VarArgsList(\n                    [\n                    FPDef([Name([\"x\"])]),\n                    \", \",\n                    \"*\",\n                    Name([\"y \"]),\n                    ]),\n                 \"-> \",\n                 Term([Factor_Power_FLReference_Atom(Name([\"y\"]))])))\n\n        self.assertParsesToTree(\"=lambda  x, **y -> y\",\n            CreateParseTree(\n                \"lambda  \",\n                 VarArgsList(\n                    [\n                    FPDef([Name([\"x\"])]),\n                    \", \",\n                    \"**\",\n                    Name([\"y \"]),\n                    ]),\n                 \"-> \",\n                 Term([Factor_Power_FLReference_Atom(Name([\"y\"]))])))\n\n        self.assertParsesToTree(\"=lambda  x, *y, **z -> y\",\n            CreateParseTree(\n                \"lambda  \",\n                 VarArgsList(\n                    [\n                    FPDef([Name([\"x\"])]),\n                    \", \",\n                    \"*\",\n                    Name([\"y\"]),\n                    \", \",\n                    \"**\",\n                    Name([\"z \"]),\n                    ]),\n                 \"-> \",\n                 Term([Factor_Power_FLReference_Atom(Name([\"y\"]))])))\n\n        self.assertParsesToTree(\"=lambda  x := None -> y\",\n            CreateParseTree(\n                \"lambda  \",\n                VarArgsList(\n                    [\n                    FPDef([Name([\"x \"])]),\n                    \":= \",\n                    TestFromAtomChild(Name([\"None \"]))\n                    ]),\n                \"-> \",\n                Term([Factor_Power_FLReference_Atom(Name([\"y\"]))])))\n\n        self.assertParsesToTree(\"=lambda  ( x ) -> y\",\n            CreateParseTree(\n                \"lambda  \",\n                VarArgsList(\n                    [\n                    FPDef([\"( \", FPList([FPDef([Name([\"x \"])])]), \") \"])\n                    ]),\n                 \"-> \",\n                 Term([Factor_Power_FLReference_Atom(Name([\"y\"]))])))\n\n        self.assertParsesToTree(\"=lambda  ( x  ,  y, z ) -> w\",\n            CreateParseTree(\n                \"lambda  \",\n                VarArgsList(\n                    [\n                    FPDef(\n                        [\n                        \"( \",\n                        FPList(\n                            [\n                            FPDef([Name([\"x  \"])]),\n                            \",  \",\n                            FPDef([Name([\"y\"])]),\n                            \", \",\n                            FPDef([Name([\"z \"])]),\n                            ]),\n                        \") \"\n                        ])\n                    ]),\n                \"-> \",\n                Term([Factor_Power_FLReference_Atom(Name([\"w\"]))])))\n\n        self.assertParsesToTree(\"=lambda  ( x  ,  y, z ), (q,  ) := 1 ,-> w\",\n            CreateParseTree(\n                \"lambda  \",\n                VarArgsList(\n                    [\n                    FPDef(\n                        [\n                        \"( \",\n                        FPList(\n                            [\n                            FPDef([Name([\"x  \"])]),\n                            \",  \",\n                            FPDef([Name([\"y\"])]),\n                            \", \",\n                            FPDef([Name([\"z \"])]),\n                            ]),\n                        \")\"\n                        ]),\n                    \", \",\n                    FPDef(\n                        [\n                        \"(\",\n                        FPList(\n                            [\n                            FPDef([Name([\"q\"])]),\n                            \",  \",\n                            ]),\n                        \") \"\n                        ]),\n                    \":= \",\n                    TestFromAtomChild(Number([\"1 \"])),\n                    \",\"\n                    ]),\n                \"-> \",\n                Term([Factor_Power_FLReference_Atom(Name([\"w\"]))])))\n\n        self.assertParsesToTree(\"=lambda  x -> y * z\",\n            CreateParseTree(\n                \"lambda  \",\n                 VarArgsList([FPDef([Name([\"x \"])])]),\n                 \"-> \",\n                 Term(\n                    [\n                    Factor_Power_FLReference_Atom(Name([\"y \"])),\n                    \"* \",\n                    Factor_Power_FLReference_Atom(Name([\"z\"]))\n                    ])))\n\n        self.assertCannotParse(\"=lambda , : 3\", 9, \", \")\n        self.assertCannotParse(\"=lambda x, y, , : 3\", 15, \", \")\n        self.assertCannotParse(\"=lambda p, x:=1, y, , : 3\", 18, \"y\")\n        self.assertCannotParse(\"=lambda *x, y, : 3\", 13, \"y\")\n        self.assertCannotParse(\"=lambda **x, y : 3\", 12, \", \")\n        self.assertCannotParse(\"=lambda *x, y:=1, : 3\", 13, \"y\")\n        self.assertCannotParse(\"=lambda **x, y:=1 : 3\", 12, \", \")\n        self.assertCannotParse(\"=lambda *x, *y : 3\", 13, \"*\")\n        self.assertCannotParse(\"=lambda **x, **y : 3\", 12, \", \")\n        self.assertCannotParse(\"=lambda **x, *y : 3\", 12, \", \")\n        self.assertCannotParse(\"=lambda z, **x, *y : 3\", 15, \", \")\n        self.assertCannotParse(\"=lambda z, *x, y : 3\", 16, \"y \")\n        self.assertCannotParse(\"=lambda *x, : 3\", 13, \": \")\n        self.assertCannotParse(\"=lambda *x, **y, : 3\", 16, \", \")\n        self.assertCannotParse(\"=lambda **y, : 3\", 12, \", \")\n        self.assertCannotParse(\"=lambda x:=1,y, : 3\", 14, \"y\")\n        self.assertCannotParse(\"=lambda (x:=1) : 3\", 11, \":=\")\n        self.assertCannotParse(\"=lambda (x:=1,y), : 3\", 11, \":=\")\n        self.assertCannotParse(\"=lambda (x:=1),y, : 3\", 11 ,\":=\")\n\n\n    def testTrivialStringConversion(self):\n        def CreateParseTree(firstQuote, atomChild, secondQuote):\n            return FLRoot([\"=\", Test_AndTest_NotTest_Comparison(\n                Expr_ConcatExpr_ShiftExpr(\n                    ArithExpr_Term(\n                        Factor([Power([FLReference([Atom(\n                            [\n                            firstQuote,\n                            TestList(\n                                [\n                                TestFromAtomChild(atomChild)\n                                ]),\n                            secondQuote\n                            ])])])]))))])\n\n\n        self.assertParsesToTree(\"=`1`\", CreateParseTree(\"`\", Number([\"1\"]), \"`\"))\n        self.assertParsesToTree(\"=` 1 ` \", CreateParseTree(\"` \", Number([\"1 \"]), \"` \"))\n        self.assertParsesToTree(\"=` fred`  \", CreateParseTree(\"` \", Name([\"fred\"]), \"`  \"))\n\n        self.assertCannotParse(\"=``\", -1)\n        self.assertCannotParse(\"=` `\", -1)\n        self.assertCannotParse(\"=`5\", -1)\n        self.assertCannotParse(\"=`5, `\", -1)\n\n\n    def testComplexStringConversion(self):\n        def CreateParseTree(firstQuote, tests, secondQuote):\n            return FLRoot([\"=\", Test_AndTest_NotTest_Comparison(\n                    Expr_ConcatExpr_ShiftExpr(\n                        ArithExpr_Term(\n                            Factor([Power([FLReference([Atom(\n                                [\n                                firstQuote,\n                                TestList(tests),\n                                secondQuote\n                                ])])])]))))])\n\n\n        self.assertParsesToTree(\n            \"=`1, 2, 3`\",\n            CreateParseTree(\n                \"`\",\n                [\n                    TestFromAtomChild(Number([\"1\"])),\n                    \", \",\n                    TestFromAtomChild(Number([\"2\"])),\n                    \", \",\n                    TestFromAtomChild(Number([\"3\"])),\n                ],\n                \"`\"))\n\n        self.assertParsesToTree(\n            \"=`1, 2, 3 `\",\n            CreateParseTree(\n                \"`\",\n                [\n                    TestFromAtomChild(Number([\"1\"])),\n                    \", \",\n                    TestFromAtomChild(Number([\"2\"])),\n                    \", \",\n                    TestFromAtomChild(Number([\"3 \"])),\n                ],\n                \"`\"))\n\n        self.assertParsesToTree(\n            \"=` cheese, 'cheese', c + heese` \",\n            CreateParseTree(\n                \"` \",\n                [\n                    TestFromAtomChild(Name([\"cheese\"])),\n                    \", \",\n                    TestFromAtomChild(StringLiteral([\"'cheese'\"])),\n                    \", \",\n                    Test_AndTest_NotTest_Comparison(\n                        Expr_ConcatExpr_ShiftExpr(\n                            ArithExpr(\n                                [\n                                Term(\n                                    [Factor_Power_FLReference_Atom(Name([\"c \"]))]),\n                                \"+ \",\n                                Term(\n                                    [Factor_Power_FLReference_Atom(Name([\"heese\"]))])\n                                ])))\n\n                ],\n                \"` \"))\n\n        self.assertCannotParse(\"=`5, 1\", -1)\n        self.assertCannotParse(\"=`5, 1,`\", -1)\n\n\n    def testEmptyParentheses(self):\n        def CreateParseTree(lParen, rParen):\n            return FLRoot([\"=\", Test_AndTest_NotTest_Comparison(\n                    Expr_ConcatExpr_ShiftExpr(\n                        ArithExpr_Term(\n                            Factor([Power([FLReference([Atom(\n                                [\n                                lParen,\n                                rParen\n                                ])])])]))))])\n\n\n        self.assertParsesToTree(\"=()\", CreateParseTree(\"(\", \")\"))\n        self.assertParsesToTree(\"=( )\", CreateParseTree(\"( \", \")\"))\n        self.assertParsesToTree(\"=() \", CreateParseTree(\"(\", \") \"))\n        self.assertParsesToTree(\"=( ) \", CreateParseTree(\"( \", \") \"))\n\n        self.assertCannotParse(\"=(\", -1)\n        self.assertCannotParse(\"=)\", 2, \")\")\n\n\n    def testParenthesesWithSingle(self):\n        def CreateParseTree(lParen, atomChild, rParen):\n            return FLRoot([\"=\", Test_AndTest_NotTest_Comparison(\n                    Expr_ConcatExpr_ShiftExpr(\n                        ArithExpr_Term(\n                            Factor([Power([FLReference([Atom(\n                                [\n                                lParen,\n                                TestListGexp(\n                                    [TestFromAtomChild(atomChild)]),\n                                rParen\n                                ])])])]))))])\n\n\n        self.assertParsesToTree(\"=(1)\", CreateParseTree(\"(\", Number([\"1\"]), \")\"))\n        self.assertParsesToTree(\"=( 1 ) \", CreateParseTree(\"( \", Number([\"1 \"]), \") \"))\n        self.assertParsesToTree(\"=( fred)  \", CreateParseTree(\"( \", Name([\"fred\"]), \")  \"))\n\n        self.assertCannotParse(\"=(5\", -1)\n        self.assertCannotParse(\"=5)\", 3, \")\")\n\n\n    def testComplexParentheses(self):\n        def CreateParseTree(lParen, testListGexp, rParen):\n            return FLRoot([\"=\", Test_AndTest_NotTest_Comparison(\n                    Expr_ConcatExpr_ShiftExpr(\n                        ArithExpr_Term(\n                            Factor([Power([FLReference([Atom(\n                                [\n                                lParen,\n                                testListGexp,\n                                rParen\n                                ])])])]))))])\n\n\n        self.assertParsesToTree(\n            \"=(1, 2, 3)\",\n            CreateParseTree(\n                \"(\",\n                TestListGexp(\n                    [\n                    TestFromAtomChild(Number([\"1\"])),\n                    \", \",\n                    TestFromAtomChild(Number([\"2\"])),\n                    \", \",\n                    TestFromAtomChild(Number([\"3\"])),\n                    ]),\n                \")\"))\n\n        self.assertParsesToTree(\n            \"=(1, 2, 3, )\",\n            CreateParseTree(\n                \"(\",\n                TestListGexp(\n                    [\n                    TestFromAtomChild(Number([\"1\"])),\n                    \", \",\n                    TestFromAtomChild(Number([\"2\"])),\n                    \", \",\n                    TestFromAtomChild(Number([\"3\"])),\n                    \", \",\n                    ]),\n                \")\"))\n\n        self.assertParsesToTree(\n            \"=( cheese, 'cheese', c + heese) \",\n            CreateParseTree(\n                \"( \",\n                TestListGexp(\n                    [\n                    TestFromAtomChild(Name([\"cheese\"])),\n                    \", \",\n                    TestFromAtomChild(StringLiteral([\"'cheese'\"])),\n                    \", \",\n                    Test_AndTest_NotTest_Comparison(\n                        Expr_ConcatExpr_ShiftExpr(\n                            ArithExpr(\n                                [\n                                Term(\n                                    [Factor_Power_FLReference_Atom(Name([\"c \"]))]),\n                                \"+ \",\n                                Term(\n                                    [Factor_Power_FLReference_Atom(Name([\"heese\"]))])\n                                ])))\n                    ]),\n                \") \"))\n\n        self.assertCannotParse(\"=(5, 1\", -1)\n        self.assertCannotParse(\"=5)\", 3, \")\")\n        self.assertCannotParse(\"=5, 1)\", 3, \", \")\n\n\n    def testSimpleGeneratorExpr(self):\n        def CreateParseTree(lBracket, test1, forStr, exprList, inStr, test2, rBracket):\n            return FLRoot([\"=\", Test_AndTest_NotTest_Comparison(\n                    Expr_ConcatExpr_ShiftExpr(\n                        ArithExpr_Term(\n                            Factor([Power([FLReference([Atom(\n                                [\n                                lBracket,\n                                TestListGexp(\n                                    [\n                                    test1,\n                                    GenFor(\n                                        [\n                                        forStr,\n                                        exprList,\n                                        inStr,\n                                        test2\n                                        ]),\n                                    ]),\n                                rBracket\n                                ])])])]))))])\n\n        self.assertParsesToTree(\"=(1 for x in L)\",\n            CreateParseTree(\n                \"(\",\n                TestFromAtomChild(Number(['1 '])),\n                \"for \",\n                ExprList(\n                    [ExprFromAtomChild(Name([\"x \"]))]),\n                \"in \",\n                TestFromAtomChild(Name([\"L\"])),\n                \")\"\n                ))\n\n        self.assertParsesToTree(\"=(lambda->x for x in L)\",\n            CreateParseTree(\n                \"(\",\n                Test(\n                    [LambDef(\n                        [\n                        \"lambda\",\n                        \"->\",\n                        TestFromAtomChild(Name([\"x \"]))\n                        ])])\n                ,\n                \"for \",\n                ExprList(\n                    [ExprFromAtomChild(Name([\"x \"]))]),\n                \"in \",\n                TestFromAtomChild(Name([\"L\"])),\n                \")\"\n                ))\n\n        self.assertParsesToTree(\"=(1 or 2 or 3 for x in L)\",\n            CreateParseTree(\n                \"(\",\n                Test(\n                    [\n                    AndTest([NotTest([Comparison([\n                                Expr_ConcatExpr_ShiftExpr(\n                                    ArithExpr_Term(\n                                        Factor_Power_FLReference_Atom(Number([\"1 \"]))))])])]),\n                    \"or \",\n                    AndTest([NotTest([Comparison([\n                                Expr_ConcatExpr_ShiftExpr(\n                                    ArithExpr_Term(\n                                        Factor_Power_FLReference_Atom(Number([\"2 \"]))))])])]),\n                    \"or \",\n                    AndTest([NotTest([Comparison([\n                                Expr_ConcatExpr_ShiftExpr(\n                                    ArithExpr_Term(\n                                        Factor_Power_FLReference_Atom(Number([\"3 \"]))))])])]),\n                    ]),\n                \"for \",\n                ExprList(\n                    [ExprFromAtomChild(Name([\"x \"]))]),\n                \"in \",\n                TestFromAtomChild(Name([\"L\"])),\n                \")\"\n                ))\n\n\n    def testComplexGeneratorExpr(self):\n        def CreateParseTree(lBracket, test1, genFor, rBracket):\n            return FLRoot([\"=\", Test_AndTest_NotTest_Comparison(\n                    Expr_ConcatExpr_ShiftExpr(\n                        ArithExpr_Term(\n                            Factor([Power([FLReference([Atom(\n                                [\n                                lBracket,\n                                TestListGexp(\n                                    [\n                                    test1,\n                                    genFor,\n                                    ]),\n                                rBracket\n                                ])])])]))))])\n\n        self.assertParsesToTree(\n            \"=(x for x in L for y in M)\",\n            CreateParseTree(\n                \"(\",\n                TestFromAtomChild(Name([\"x \"])),\n                GenFor(\n                    [\n                    \"for \",\n                    ExprList(\n                        [ExprFromAtomChild(Name([\"x \"]))]),\n                    \"in \",\n                    TestFromAtomChild(Name([\"L \"])),\n                    GenIter(\n                        [GenFor(\n                            [\n                            \"for \",\n                            ExprList(\n                                [ExprFromAtomChild(Name([\"y \"]))]),\n                            \"in \",\n                            TestFromAtomChild(Name([\"M\"])),\n                            ])])\n                    ]),\n                \")\"))\n\n        self.assertParsesToTree(\n            \"=(x for x in L if a)\",\n            CreateParseTree(\n                \"(\",\n                TestFromAtomChild(Name([\"x \"])),\n                GenFor(\n                    [\n                    \"for \",\n                    ExprList(\n                        [ExprFromAtomChild(Name([\"x \"]))]),\n                    \"in \",\n                    TestFromAtomChild(Name([\"L \"])),\n                    GenIter(\n                        [GenIf(\n                            [\n                            \"if \",\n                            TestFromAtomChild(Name([\"a\"])),\n                            ])])\n                    ]),\n                \")\"))\n\n        self.assertParsesToTree(\n            \"=(x for x in L if a if b for q in M)\",\n            CreateParseTree(\n                \"(\",\n                TestFromAtomChild(Name([\"x \"])),\n                GenFor(\n                    [\n                    \"for \",\n                    ExprList(\n                        [ExprFromAtomChild(Name([\"x \"]))]),\n                    \"in \",\n                    TestFromAtomChild(Name([\"L \"])),\n                    GenIter(\n                        [GenIf(\n                            [\n                            \"if \",\n                            TestFromAtomChild(Name([\"a \"])),\n                            GenIter(\n                                [GenIf(\n                                    [\n                                    \"if \",\n                                    TestFromAtomChild(Name([\"b \"])),\n                                    GenIter(\n                                        [GenFor(\n                                            [\n                                            \"for \",\n                                            ExprList(\n                                                [ExprFromAtomChild(Name([\"q \"]))]),\n                                            \"in \",\n                                            TestFromAtomChild(Name([\"M\"])),\n                                            ])])\n                                    ])])\n                            ])])\n                    ]),\n                \")\"))\n\n\n    def testEmptyList(self):\n        def CreateParseTree(lBracket, rBracket):\n            return FLRoot([\"=\", Test_AndTest_NotTest_Comparison(\n                    Expr_ConcatExpr_ShiftExpr(\n                        ArithExpr_Term(\n                            Factor([Power([FLReference([Atom(\n                                [\n                                lBracket,\n                                rBracket\n                                ])])])]))))])\n\n        self.assertParsesToTree(\"=[ ]\", CreateParseTree('[ ', ']'))\n        self.assertParsesToTree(\"=[ ] \", CreateParseTree('[ ', '] '))\n        self.assertParsesToTree(\"=[]\", CreateParseTree('[', ']'))\n        self.assertParsesToTree(\"=[] \", CreateParseTree('[', '] '))\n\n        self.assertCannotParse(\"=[\", -1)\n        self.assertCannotParse(\"=]\", 2, \"]\")\n        self.assertCannotParse(\"=[[\", -1)\n        self.assertCannotParse(\"=][\", 2, \"]\")\n\n\n    def testListWithNumbers(self):\n        def CreateParseTree(lBracket, tests, rBracket):\n            return FLRoot([\"=\", Test_AndTest_NotTest_Comparison(\n                    Expr_ConcatExpr_ShiftExpr(\n                        ArithExpr_Term(\n                            Factor([Power([FLReference([Atom(\n                                [\n                                lBracket,\n                                ListMaker(tests),\n                                rBracket\n                                ])])])]))))])\n\n        self.assertParsesToTree(\"=[ 3 ]\",\n            CreateParseTree('[ ', [TestFromAtomChild(Number(['3 ']))], ']'))\n        self.assertParsesToTree(\"=[ 3 ,]\",\n            CreateParseTree('[ ', [TestFromAtomChild(Number(['3 '])), \",\"], ']'))\n        self.assertParsesToTree(\"=[ 3, 4 ]\",\n            CreateParseTree(\n                '[ ',\n                [\n                    TestFromAtomChild(Number(['3'])),\n                    \", \",\n                    TestFromAtomChild(Number(['4 '])),\n                ],\n                ']'))\n        self.assertParsesToTree(\"=[ 3, 4 ,] \",\n            CreateParseTree(\n                '[ ',\n                [\n                    TestFromAtomChild(Number(['3'])),\n                    \", \",\n                    TestFromAtomChild(Number(['4 '])),\n                    \",\"\n                ],\n                '] '))\n\n        self.assertCannotParse(\"=[3 3]\", 5, \"3\")\n        self.assertCannotParse(\"=[3:3]\", 4, \":\")\n\n\n    def testListWithSimple(self):\n        \"test list with simple for\"\n        def CreateParseTree(lBracket, test, forStr, exprList, inStr, testListSafe, rBracket):\n            return FLRoot([\"=\", Test_AndTest_NotTest_Comparison(\n                    Expr_ConcatExpr_ShiftExpr(\n                        ArithExpr_Term(\n                            Factor([Power([FLReference([Atom(\n                                [\n                                lBracket,\n                                ListMaker(\n                                    [\n                                    test,\n                                    ListFor(\n                                        [\n                                        forStr,\n                                        exprList,\n                                        inStr,\n                                        testListSafe\n                                        ])\n                                    ]),\n                                rBracket\n                                ])])])]))))])\n\n        self.assertParsesToTree(\"=[1 for x in L]\",\n            CreateParseTree(\n                \"[\",\n                TestFromAtomChild(Number(['1 '])),\n                \"for \",\n                ExprList(\n                    [ExprFromAtomChild(Name([\"x \"]))]),\n                \"in \",\n                TestList(\n                    [Test_AndTest_NotTest_Comparison(\n                        Expr_ConcatExpr_ShiftExpr(\n                            ArithExpr_Term(\n                                Factor_Power_FLReference_Atom(Name([\"L\"])))))]),\n                \"]\"\n                ))\n\n        self.assertParsesToTree(\"=[1 for x, y in L]\",\n            CreateParseTree(\n                \"[\",\n                TestFromAtomChild(Number(['1 '])),\n                \"for \",\n                ExprList(\n                    [\n                    ExprFromAtomChild(Name([\"x\"])),\n                    \", \",\n                    ExprFromAtomChild(Name([\"y \"]))\n                    ]),\n                \"in \",\n                TestList(\n                    [Test_AndTest_NotTest_Comparison(\n                        Expr_ConcatExpr_ShiftExpr(\n                            ArithExpr_Term(\n                                Factor_Power_FLReference_Atom(Name([\"L\"])))))]),\n                \"]\"\n                ))\n\n\n    def testListWithParenthetical(self):\n        \"test list with parenthetical for\"\n        self.assertParsesToTree(\"=[1 for (x, y) in L]\",\n            CreateParseTree(\n                \"[\",\n                TestFromAtomChild(Number(['1 '])),\n                \"for \",\n                ExprList(\n                    [Expr_ConcatExpr_ShiftExpr(\n                        ArithExpr_Term(\n                            Factor([Power([FLReference([Atom(\n                                [\n                                \"(\",\n                                TestListGexp(\n                                    [\n                                    TestFromAtomChild(Name([\"x\"])),\n                                    \", \",\n                                    TestFromAtomChild(Name([\"y\"]))\n                                    ]),\n                                \") \"\n                                ])])])])))]),\n                \"in \",\n                TestList(\n                    [Test_AndTest_NotTest_Comparison(\n                        Expr_ConcatExpr_ShiftExpr(\n                            ArithExpr_Term(\n                                Factor_Power_FLReference_Atom(Name([\"L\"])))))]),\n                \"]\"\n                ))\n\n        self.assertParsesToTree(\"=[1 for [x, y, z,], in L]\",\n            CreateParseTree(\n                \"[\",\n                TestFromAtomChild(Number(['1 '])),\n                \"for \",\n                ExprList(\n                    [\n                    Expr_ConcatExpr_ShiftExpr(\n                        ArithExpr_Term(\n                            Factor([Power([FLReference([Atom(\n                                [\n                                \"[\",\n                                ListMaker(\n                                    [\n                                    TestFromAtomChild(Name([\"x\"])),\n                                    \", \",\n                                    TestFromAtomChild(Name([\"y\"])),\n                                    \", \",\n                                    TestFromAtomChild(Name([\"z\"])),\n                                    \",\"\n                                    ]),\n                                \"]\"\n                                ])])])]))),\n                    \", \"\n                    ]),\n                \"in \",\n                TestList(\n                    [Test_AndTest_NotTest_Comparison(\n                        Expr_ConcatExpr_ShiftExpr(\n                            ArithExpr_Term(\n                                Factor_Power_FLReference_Atom(Name([\"L\"])))))]),\n                \"]\"\n                ))\n        _level2 = Test_AndTest_NotTest_Comparison(\n                    Expr_ConcatExpr_ShiftExpr(\n                        ArithExpr_Term(\n                            Factor([Power([FLReference([Atom(\n                                [\n                                \"[\",\n                                ListMaker(\n                                    [\n                                    TestFromAtomChild(Name([\"y\"])),\n                                    \", \",\n                                    TestFromAtomChild(Name([\"z\"])),\n                                    \",\"\n                                    ]),\n                                \"]\"\n                                ])])])]))))\n        self.assertParsesToTree(\"=[1 for (x, [y, z,]), in L]\",\n            CreateParseTree(\n                \"[\",\n                TestFromAtomChild(Number(['1 '])),\n                \"for \",\n                ExprList(\n                    [\n                    Expr_ConcatExpr_ShiftExpr(\n                        ArithExpr_Term(\n                            Factor([Power([FLReference([Atom(\n                                [\n                                \"(\",\n                                TestListGexp(\n                                    [\n                                    TestFromAtomChild(Name([\"x\"])),\n                                    \", \",\n                                    _level2\n                                    ]),\n                                \")\"\n                                ])])])]))),\n                    \", \"\n                    ]),\n                \"in \",\n                TestList(\n                    [Test_AndTest_NotTest_Comparison(\n                        Expr_ConcatExpr_ShiftExpr(\n                            ArithExpr_Term(\n                                Factor_Power_FLReference_Atom(Name([\"L\"])))))]),\n                \"]\"\n                ))\n\n        _level2 = Expr_ConcatExpr_ShiftExpr(\n                    ArithExpr_Term(\n                        Factor([Power([FLReference([Atom(\n                            [\n                            \"(\",\n                            TestListGexp(\n                                [\n                                TestFromAtomChild(Name([\"x\"])),\n                                \", \",\n\n                                Test_AndTest_NotTest_Comparison(\n                                    Expr_ConcatExpr_ShiftExpr(\n                                        ArithExpr_Term(\n                                            Factor([Power([FLReference([Atom(\n                                                [\n                                                \"[\",\n                                                ListMaker(\n                                                    [\n                                                    TestFromAtomChild(Name([\"y\"])),\n                                                    \", \",\n                                                    TestFromAtomChild(Name([\"z\"])),\n                                                    \",\"\n                                                    ]),\n                                                \"]\"\n                                                ])])])]))))\n                                ]),\n                            \")\"\n                            ])])])])))\n        self.assertParsesToTree(\"=[1 for (x, [y, z,]), in L, M]\",\n            CreateParseTree(\n                \"[\",\n                TestFromAtomChild(Number(['1 '])),\n                \"for \",\n                ExprList(\n                    [\n                    _level2,\n                    \", \"\n                    ]),\n                \"in \",\n                TestList(\n                    [\n                    Test_AndTest_NotTest_Comparison(\n                        Expr_ConcatExpr_ShiftExpr(\n                            ArithExpr_Term(\n                                Factor_Power_FLReference_Atom(Name([\"L\"]))))),\n                    \", \",\n                    Test_AndTest_NotTest_Comparison(\n                        Expr_ConcatExpr_ShiftExpr(\n                            ArithExpr_Term(\n                                Factor_Power_FLReference_Atom(Name([\"M\"])))))\n                    ]),\n                \"]\"\n                ))\n\n        _level2 = TestListGexp(\n                    [\n                    TestFromAtomChild(Name([\"x\"])),\n                    \", \",\n\n                    Test_AndTest_NotTest_Comparison(\n                        Expr_ConcatExpr_ShiftExpr(\n                            ArithExpr_Term(\n                                Factor([Power([FLReference([Atom(\n                                    [\n                                    \"[\",\n                                    ListMaker(\n                                        [\n                                        TestFromAtomChild(Name([\"y\"])),\n                                        \", \",\n                                        TestFromAtomChild(Name([\"z\"])),\n                                        \",\"\n                                        ]),\n                                    \"]\"\n                                    ])])])]))))\n                    ])\n        self.assertParsesToTree(\"=[1 for (x, [y, z,]), in L or M]\",\n            CreateParseTree(\n                \"[\",\n                TestFromAtomChild(Number(['1 '])),\n                \"for \",\n                ExprList(\n                    [\n                    Expr_ConcatExpr_ShiftExpr(\n                        ArithExpr_Term(\n                            Factor([Power([FLReference([Atom(\n                                [\n                                \"(\",\n                                _level2,\n                                \")\"\n                                ])])])]))),\n                    \", \"\n                    ]),\n                \"in \",\n                TestList(\n                    [\n                    Test(\n                        [\n                        AndTest([NotTest([Comparison(\n                            [Expr_ConcatExpr_ShiftExpr(\n                                ArithExpr_Term(\n                                    Factor_Power_FLReference_Atom(Name([\"L \"]))))])])]),\n                        \"or \",\n                        AndTest([NotTest([Comparison(\n                            [Expr_ConcatExpr_ShiftExpr(\n                                ArithExpr_Term(\n                                    Factor_Power_FLReference_Atom(Name([\"M\"]))))])])]),\n                        ])\n                    ]),\n                \"]\"\n                ))\n\n        _level2 = TestListGexp(\n                    [\n                    TestFromAtomChild(Name([\"x\"])),\n                    \", \",\n\n                    Test_AndTest_NotTest_Comparison(\n                        Expr_ConcatExpr_ShiftExpr(\n                            ArithExpr_Term(\n                                Factor([Power([FLReference([Atom(\n                                    [\n                                    \"[\",\n                                    ListMaker(\n                                        [\n                                        TestFromAtomChild(Name([\"y\"])),\n                                        \", \",\n                                        TestFromAtomChild(Name([\"z\"])),\n                                        \",\"\n                                        ]),\n                                    \"]\"\n                                    ])])])]))))\n                    ])\n        self.assertParsesToTree(\"=[1 for (x, [y, z,]), in L or M or N]\",\n            CreateParseTree(\n                \"[\",\n                TestFromAtomChild(Number(['1 '])),\n                \"for \",\n                ExprList(\n                    [\n                    Expr_ConcatExpr_ShiftExpr(\n                        ArithExpr_Term(\n                            Factor([Power([FLReference([Atom(\n                                [\n                                \"(\",\n                                    _level2,\n                                \")\"\n                                ])])])]))),\n                    \", \"\n                    ]),\n                \"in \",\n                TestList(\n                    [\n                    Test(\n                        [\n                        AndTest([NotTest([Comparison(\n                            [Expr_ConcatExpr_ShiftExpr(\n                                ArithExpr_Term(\n                                    Factor_Power_FLReference_Atom(Name([\"L \"]))))])])]),\n                        \"or \",\n                        AndTest([NotTest([Comparison(\n                            [Expr_ConcatExpr_ShiftExpr(\n                                ArithExpr_Term(\n                                    Factor_Power_FLReference_Atom(Name([\"M \"]))))])])]),\n                        \"or \",\n                        AndTest([NotTest([Comparison(\n                            [Expr_ConcatExpr_ShiftExpr(\n                                ArithExpr_Term(\n                                    Factor_Power_FLReference_Atom(Name([\"N\"]))))])])]),\n                        ])\n                    ]),\n                \"]\"\n                ))\n\n        _level2 = TestListGexp(\n                    [\n                    TestFromAtomChild(Name([\"x\"])),\n                    \", \",\n\n                    Test_AndTest_NotTest_Comparison(\n                        Expr_ConcatExpr_ShiftExpr(\n                            ArithExpr_Term(\n                                Factor([Power([FLReference([Atom(\n                                    [\n                                    \"[\",\n                                    ListMaker(\n                                        [\n                                        TestFromAtomChild(Name([\"y\"])),\n                                        \", \",\n                                        TestFromAtomChild(Name([\"z\"])),\n                                        \",\"\n                                        ]),\n                                    \"]\"\n                                    ])])])]))))\n                    ])\n        self.assertParsesToTree(\"=[1 for (x, [y, z,]), in lambda -> 1]\",\n            CreateParseTree(\n                \"[\",\n                TestFromAtomChild(Number(['1 '])),\n                \"for \",\n                ExprList(\n                    [\n                    Expr_ConcatExpr_ShiftExpr(\n                        ArithExpr_Term(\n                            Factor([Power([FLReference([Atom(\n                                [\n                                \"(\",\n                                    _level2,\n                                \")\"\n                                ])])])]))),\n                    \", \"\n                    ]),\n                \"in \",\n                TestList(\n                    [Test(\n                        [LambDef(\n                            [\n                            \"lambda \",\n                            \"-> \",\n                            TestFromAtomChild(Number([\"1\"]))\n                            ])])]),\n                \"]\"\n                ))\n\n        self.assertCannotParse(\"=[1 for (x, [y, z,)], in L]\", 19, \")\")\n        self.assertCannotParse(\"=[1 for (x, [y, z,]), in L or M,]\", 33, \"]\")\n        self.assertCannotParse(\"=[1 for (x, [y, z,]), in L, M,]\", 31, \"]\") # deviation from Python!\n\n\n    def testListWithList(self):\n        \"test list with list iter\"\n        def CreateParseTree(lBracket, test, listFor, rBracket):\n            return FLRoot([\"=\", Test_AndTest_NotTest_Comparison(\n                    Expr_ConcatExpr_ShiftExpr(\n                        ArithExpr_Term(\n                            Factor([Power([FLReference([Atom(\n                                [\n                                lBracket,\n                                ListMaker(\n                                    [\n                                    test,\n                                    listFor\n                                    ]),\n                                rBracket\n                                ])])])]))))])\n\n        self.assertParsesToTree(\n            \"=[x for x in L for y in M]\",\n            CreateParseTree(\n                \"[\",\n                TestFromAtomChild(Name([\"x \"])),\n                ListFor(\n                    [\n                    \"for \",\n                    ExprList([ExprFromAtomChild(Name([\"x \"]))]),\n                    \"in \",\n                    TestList(\n                        [Test(\n                            [AndTest(\n                                [NotTest(\n                                    [Comparison(\n                                        [Expr_ConcatExpr_ShiftExpr(\n                                            ArithExpr_Term(\n                                                Factor_Power_FLReference_Atom(Name([\"L \"]))))])])])])]),\n                    ListIter(\n                        [ListFor(\n                            [\n                            \"for \",\n                            ExprList([ExprFromAtomChild(Name([\"y \"]))]),\n                            \"in \",\n                            TestList(\n                                [Test(\n                                    [AndTest(\n                                        [NotTest(\n                                            [Comparison(\n                                                [Expr_ConcatExpr_ShiftExpr(\n                                                    ArithExpr_Term(\n                                                        Factor_Power_FLReference_Atom(Name([\"M\"]))))])])])])]),\n                            ])])\n                    ]),\n                \"]\")\n            )\n\n        self.assertParsesToTree(\n            \"=[x for x in L if Y]\",\n            CreateParseTree(\n                \"[\",\n                TestFromAtomChild(Name([\"x \"])),\n                ListFor(\n                    [\n                    \"for \",\n                    ExprList([ExprFromAtomChild(Name([\"x \"]))]),\n                    \"in \",\n                    TestList(\n                        [Test(\n                            [AndTest(\n                                [NotTest(\n                                    [Comparison(\n                                        [Expr_ConcatExpr_ShiftExpr(\n                                            ArithExpr_Term(\n                                                Factor_Power_FLReference_Atom(Name([\"L \"]))))])])])])]),\n                    ListIter(\n                        [ListIf(\n                            [\n                            \"if \",\n                            Test(\n                                [AndTest(\n                                    [NotTest(\n                                        [Comparison(\n                                            [Expr_ConcatExpr_ShiftExpr(\n                                                ArithExpr_Term(\n                                                    Factor_Power_FLReference_Atom(Name([\"Y\"]))))])])])])\n                            ])])\n                    ]),\n                \"]\")\n            )\n\n        self.assertParsesToTree(\n            \"=[x for x in L if Y for a in b]\",\n            CreateParseTree(\n                \"[\",\n                TestFromAtomChild(Name([\"x \"])),\n                ListFor(\n                    [\n                    \"for \",\n                    ExprList([ExprFromAtomChild(Name([\"x \"]))]),\n                    \"in \",\n                    TestList(\n                        [Test(\n                            [AndTest(\n                                [NotTest(\n                                    [Comparison(\n                                        [Expr_ConcatExpr_ShiftExpr(\n                                            ArithExpr_Term(\n                                                Factor_Power_FLReference_Atom(Name([\"L \"]))))])])])])]),\n                    ListIter(\n                        [ListIf(\n                            [\n                            \"if \",\n                            Test(\n                                [AndTest(\n                                    [NotTest(\n                                        [Comparison(\n                                            [Expr_ConcatExpr_ShiftExpr(\n                                                ArithExpr_Term(\n                                                    Factor_Power_FLReference_Atom(Name([\"Y \"]))))])])])]),\n                            ListIter(\n                                [ListFor(\n                                    [\n                                    \"for \",\n                                    ExprList([ExprFromAtomChild(Name([\"a \"]))]),\n                                    \"in \",\n                                    TestList(\n                                        [Test(\n                                            [AndTest(\n                                                [NotTest(\n                                                    [Comparison(\n                                                        [Expr_ConcatExpr_ShiftExpr(\n                                                            ArithExpr_Term(\n                                                                Factor_Power_FLReference_Atom(Name([\"b\"]))))])])])])]),\n                                    ])])\n                            ])])\n                    ]),\n                \"]\")\n            )\n\n        self.assertParsesToTree(\n            \"=[x for x in L if Y if Z]\",\n            CreateParseTree(\n                \"[\",\n                TestFromAtomChild(Name([\"x \"])),\n                ListFor(\n                    [\n                    \"for \",\n                    ExprList([ExprFromAtomChild(Name([\"x \"]))]),\n                    \"in \",\n                    TestList(\n                        [Test(\n                            [AndTest(\n                                [NotTest(\n                                    [Comparison(\n                                        [Expr_ConcatExpr_ShiftExpr(\n                                            ArithExpr_Term(\n                                                Factor_Power_FLReference_Atom(Name([\"L \"]))))])])])])]),\n                    ListIter(\n                        [ListIf(\n                            [\n                            \"if \",\n                            Test(\n                                [AndTest(\n                                    [NotTest(\n                                        [Comparison(\n                                            [Expr_ConcatExpr_ShiftExpr(\n                                                ArithExpr_Term(\n                                                    Factor_Power_FLReference_Atom(Name([\"Y \"]))))])])])]),\n                            ListIter(\n                                [ListIf(\n                                    [\n                                    \"if \",\n                                    Test(\n                                        [AndTest(\n                                            [NotTest(\n                                                [Comparison(\n                                                    [Expr_ConcatExpr_ShiftExpr(\n                                                        ArithExpr_Term(\n                                                            Factor_Power_FLReference_Atom(Name([\"Z\"]))))])])])])\n                                    ])])\n                            ])])\n                    ]),\n                \"]\")\n            )\n\n\n    def testEmptyDict(self):\n        def CreateParseTree(lBrace, rBrace):\n            return FLRoot([\"=\", Test_AndTest_NotTest_Comparison(\n                    Expr_ConcatExpr_ShiftExpr(\n                        ArithExpr_Term(\n                            Factor([Power([FLReference([Atom(\n                                [\n                                lBrace,\n                                rBrace\n                                ])])])]))))])\n\n\n        self.assertParsesToTree(\"={}\", CreateParseTree(\"{\", \"}\"))\n        self.assertParsesToTree(\"={ }\", CreateParseTree(\"{ \", \"}\"))\n        self.assertParsesToTree(\"={} \", CreateParseTree(\"{\", \"} \"))\n        self.assertParsesToTree(\"={ } \", CreateParseTree(\"{ \", \"} \"))\n\n        self.assertCannotParse(\"={\", -1)\n        self.assertCannotParse(\"=}\", 2, \"}\")\n\n\n    def testSimpleDict(self):\n        def CreateParseTree(lBrace, keyAtomChild, littleArrow, valueAtomChild, rBrace):\n            return FLRoot([\"=\", Test_AndTest_NotTest_Comparison(\n                    Expr_ConcatExpr_ShiftExpr(\n                        ArithExpr_Term(\n                            Factor([Power([FLReference([Atom(\n                                [\n                                lBrace,\n                                DictMaker(\n                                    [\n                                    TestFromAtomChild(keyAtomChild),\n                                    littleArrow,\n                                    TestFromAtomChild(valueAtomChild)\n                                    ]),\n                                rBrace\n                                ])])])]))))])\n\n\n        self.assertParsesToTree(\"={1->2}\", CreateParseTree(\"{\", Number([\"1\"]), \"->\", Number([\"2\"]), \"}\"))\n        self.assertParsesToTree(\"={ 1 -> 'cheese' } \", CreateParseTree(\"{ \", Number([\"1 \"]), \"-> \", StringLiteral([\"'cheese' \"]), \"} \"))\n        self.assertParsesToTree(\"={ '' ->fred}  \", CreateParseTree(\"{ \", StringLiteral([\"'' \"]), \"->\", Name([\"fred\"]), \"}  \"))\n\n        self.assertCannotParse(\"={5\", -1)\n        self.assertCannotParse(\"={5}\", 4, \"}\")\n        self.assertCannotParse(\"={5->}\", 6, \"}\")\n        self.assertCannotParse(\"={->5}\", 3, \"->\")\n\n\n    def testComplexDictDisplay(self):\n        def CreateParseTree(lBrace, dictMaker, rBrace):\n            return FLRoot([\"=\", Test_AndTest_NotTest_Comparison(\n                    Expr_ConcatExpr_ShiftExpr(\n                        ArithExpr_Term(\n                            Factor([Power([FLReference([Atom(\n                                [\n                                lBrace,\n                                dictMaker,\n                                rBrace\n                                ])])])]))))])\n\n        self.assertParsesToTree(\n            \"={1->2,}\",\n            CreateParseTree(\n                \"{\",\n                DictMaker(\n                    [\n                    TestFromAtomChild(Number([\"1\"])),\n                    \"->\",\n                    TestFromAtomChild(Number([\"2\"])),\n                    \",\",\n                    ]),\n                \"}\"))\n\n        self.assertParsesToTree(\n            \"={1->2,3->4}\",\n            CreateParseTree(\n                \"{\",\n                DictMaker(\n                    [\n                    TestFromAtomChild(Number([\"1\"])),\n                    \"->\",\n                    TestFromAtomChild(Number([\"2\"])),\n                    \",\",\n                    TestFromAtomChild(Number([\"3\"])),\n                    \"->\",\n                    TestFromAtomChild(Number([\"4\"])),\n                    ]),\n                \"}\"))\n\n        self.assertParsesToTree(\n            \"={ 1 -> 2 , 'foo' '''bar''' -> angry  ,} \",\n            CreateParseTree(\n                \"{ \",\n                DictMaker(\n                    [\n                    TestFromAtomChild(Number([\"1 \"])),\n                    \"-> \",\n                    TestFromAtomChild(Number([\"2 \"])),\n                    \", \",\n                    TestFromAtomChild(StringLiteral([\"'foo' \", \"'''bar''' \"])),\n                    \"-> \",\n                    TestFromAtomChild(Name([\"angry  \"])),\n                    \",\"\n                    ]),\n                \"} \"))\n\n        self.assertParsesToTree(\n            \"={ 1 -> c + 'heese' , }\",\n            CreateParseTree(\n                \"{ \",\n                DictMaker(\n                    [\n                    TestFromAtomChild(Number([\"1 \"])),\n                    \"-> \",\n                    Test_AndTest_NotTest_Comparison(\n                        Expr_ConcatExpr_ShiftExpr(\n                            ArithExpr(\n                                [\n                                Term(\n                                    [Factor_Power_FLReference_Atom(Name([\"c \"]))]),\n                                \"+ \",\n                                Term(\n                                    [Factor_Power_FLReference_Atom(StringLiteral([\"'heese' \"]))])\n                                ]))),\n                    \", \"\n                    ]),\n                \"}\"))\n\n\n        self.assertCannotParse(\"=(5, 1\", -1)\n\n\n    def testOtherErrors(self):\n        self.assertCannotParse(\"=b6+'64654\", 5, \"'64654\")\n        self.assertCannotParse(\"=\\\\\", 2, \"\\\\\")\n        self.assertCannotParse(\"=hello'world'\", 7, \"'world'\")\n\n\n    def testIfFunctionCalls(self):\n        def CreateParseTree(nameChild, trailers):\n            return FLRoot([\"=\", TestFromFlReferenceChildren([Atom([Name([nameChild])]), Trailer(trailers)])])\n\n\n        self.assertCannotParse(\"=if(a)\", 6, ')')\n        self.assertCannotParse(\"=if(a, b, c, d)\", 12, ', ')\n        self.assertParsesToTree(\n            \"=If(1, 2, 3)\",\n            CreateParseTree(\"If\",\n                ['(',\n                 Argument([TestFromAtomChild(Number([\"1\"]))]),\n                 ', ',\n                 Argument([TestFromAtomChild(Number([\"2\"]))]),\n                 ', ',\n                 Argument([TestFromAtomChild(Number([\"3\"]))]),\n                 ')'])\n            )\n        self.assertParsesToTree(\n            \"=if(1, 2, 3)()\",\n            FLRoot(\n                [\n                    \"=\",\n                    TestFromFlReferenceChildren(\n                        [\n                            Atom([Name([\"if\"])]),\n                            Trailer(\n                                [\n                                    '(',\n                                    Argument([TestFromAtomChild(Number([\"1\"]))]),\n                                    ', ',\n                                    Argument([TestFromAtomChild(Number([\"2\"]))]),\n                                    ', ',\n                                    Argument([TestFromAtomChild(Number([\"3\"]))]),\n                                    ')'\n                                ]\n                            ),\n                            Trailer(['(', ')'])\n                        ]\n                    )\n                ]\n            )\n        )\n\n        self.assertParsesToTree(\n            \"=iF(A1:B1, B2, C2)\",\n            CreateParseTree(\"iF\",\n                ['(',\n                 ArgumentFromFLReferenceChild(FLCellRange(\n                    [\n                    FLCellReference([\"A1\"]),\n                    \":\",\n                    FLCellReference([\"B1\"])\n                    ])),\n                 ', ',\n                 ArgumentFromFLReferenceChild(FLCellReference([\"B2\"])),\n                 ', ',\n                 ArgumentFromFLReferenceChild(FLCellReference([\"C2\"])),\n                 ')'])\n            )\n\n        self.assertParsesToTree(\n            \"=If(a=1, 2, 3)\",\n            CreateParseTree(\"If\",\n                ['(',\n                 Argument([CreateComparison(Name(['a']), '=', Number(['1']))]),\n                 ', ',\n                 Argument([TestFromAtomChild(Number([\"2\"]))]),\n                 ', ',\n                 Argument([TestFromAtomChild(Number([\"3\"]))]),\n                 ')'])\n            )\n        self.assertParsesToTree(\n            \"=If(a=1, 2,)\",\n            CreateParseTree(\"If\",\n                ['(',\n                 Argument([CreateComparison(Name(['a']), '=', Number(['1']))]),\n                 ', ',\n                 Argument([TestFromAtomChild(Number([\"2\"]))]),\n                 ',',\n                 ')'])\n            )\n        self.assertParsesToTree(\n            \"=If(a=1, 2)\",\n            CreateParseTree(\"If\",\n                ['(',\n                 Argument([CreateComparison(Name(['a']), '=', Number(['1']))]),\n                 ', ',\n                 Argument([TestFromAtomChild(Number([\"2\"]))]),\n                 ')'])\n            )\n\n\n\n        def Sum(name, lBracket, argList, rBracket):\n            return Argument([CreateTest(name, lBracket, argList, rBracket)])\n\n        self.assertParsesToTree(\n            \"=if(sum(A1:B1), functionCall(1, 2), sum for sum in L if sum)\",\n            CreateParseTree(\"if\",\n                ['(',\n                 Sum(\n                    \"sum\",\n                    \"(\",\n                    [\n                        ArgumentFromFLReferenceChild(FLCellRange(\n                            [\n                            FLCellReference([\"A1\"]),\n                            \":\",\n                            FLCellReference([\"B1\"])\n                            ]))\n                    ],\n                    \")\"),\n                  \", \",\n                  Argument([TestFromFlReferenceChildren(\n                        [Atom([Name([\"functionCall\"])]),\n                         Trailer([\"(\",\n                                  ArgList([Argument([TestFromAtomChild(Number([\"1\"]))]),\n                                           \", \",\n                                           Argument([TestFromAtomChild(Number([\"2\"]))])\n                                           ]),\n                                  \")\"\n                              ])\n                         ])]),\n                 \", \",\n                 Argument([TestFromAtomChild(Name([\"sum \"])),\n                           GenFor(\n                               [\n                               \"for \",\n                               ExprList(\n                                   [ExprFromAtomChild(Name([\"sum \"]))]),\n                               \"in \",\n                               TestFromAtomChild(Name([\"L \"])),\n                               GenIter(\n                                   [GenIf(\n                                       [\n                                       \"if \",\n                                       TestFromAtomChild(Name([\"sum\"])),\n                                       ])])\n\n                ])]),\n                 ')'\n                ]\n            )\n        )\n\n\n    def testAndOrFunctions(self):\n        self.checkFunction(\"and\")\n        self.checkFunction(\"or\")\n\n\n    def testISE(self):\n        \"test i s e r r o r function calls\"\n        def CreateParseTree(nameChild, trailers):\n            return FLRoot([\"=\", TestFromFlReferenceChildren([Atom([Name([nameChild])]), Trailer(trailers)])])\n\n\n        self.assertCannotParse(\"=iserror()\", 10, ')')\n        self.assertCannotParse(\"=iserror(a, b)\", 11, ', ')\n\n        self.assertParsesToTree(\n            \"=isError(1)\",\n            CreateParseTree(\"isError\",\n                ['(',\n                 Argument([Test_AndTest_NotTest_Comparison(ExprFromAtomChild(Number([\"1\"])))]),\n                 ')'])\n            )\n\n        self.assertParsesToTree(\n            \"=iserrOR(A1)\",\n            CreateParseTree(\"iserrOR\",\n                ['(',\n                 ArgumentFromFLReferenceChild(FLCellReference([\"A1\"])),\n                 ')'])\n            )\n\n        self.assertParsesToTree(\n            \"=iserror(9 and 9)\",\n            CreateParseTree(\"iserror\",\n                ['(',\n                 Argument([CreateAndTest(Number([\"9 \"]), \"and \", Number([\"9\"]))]),\n                 ')'])\n            )\n\n\n    def checkFunction(self, functionName):\n        def CreateParseTree(nameChild, trailers):\n            return FLRoot([\"=\", TestFromFlReferenceChildren([Atom([Name([nameChild])]), Trailer(trailers)])])\n\n        upper = functionName.upper()\n        self.assertCannotParse(\"=%s\" % upper, -1, \"\")\n        self.assertCannotParse(\"=%s()\" % upper, len(upper) + 3, \")\")\n        self.assertParsesToTree(\n            \"=%s(1)\" % upper,\n            CreateParseTree(upper, ['(', ArgList([Argument([TestFromAtomChild(Number([\"1\"]))])]), ')'])\n            )\n        self.assertParsesToTree(\n            \"=%s(1, 2, 3)\" % functionName,\n            CreateParseTree(functionName,\n                ['(', ArgList([\n                    Argument([TestFromAtomChild(Number([\"1\"]))]),\n                    ', ',\n                    Argument([TestFromAtomChild(Number([\"2\"]))]),\n                    ', ',\n                    Argument([TestFromAtomChild(Number([\"3\"]))])\n                    ]),\n                 ')'])\n            )\n\n        self.assertParsesToTree(\n            \"=%s(A1)\" % functionName.title(),\n            CreateParseTree(functionName.title(),\n                ['(', ArgList([\n                    ArgumentFromFLReferenceChild(FLCellReference([\"A1\"]))]),\n                 ')'])\n            )\n\n        self.assertParsesToTree(\n            \"=%s(A1:B1)\" % upper,\n            CreateParseTree(upper,\n                ['(', ArgList([\n                    ArgumentFromFLReferenceChild(\n                        FLCellRange(\n                            [\n                            FLCellReference([\"A1\"]),\n                            \":\",\n                            FLCellReference([\"B1\"])\n                            ]))]),\n                 ')'])\n            )\n\n        self.assertParsesToTree(\n            \"=%s(6 and 6, A3)\" % upper,\n            CreateParseTree(upper,\n                ['(', ArgList([\n                    Argument([\n                        CreateAndTest(Number([\"6 \"]), \"and \", Number([\"6\"]))\n                        ]),\n\n                    \", \",\n                    ArgumentFromFLReferenceChild(FLCellReference([\"A3\"]))\n                    ]),\n                 ')'])\n            )\n\n\n    def testCannotUseInappropriate(self):\n        \"test cannot use inappropriate keywords in expressions\"\n        def AssertKeywordError(keyword):\n            try:\n                parse(\"=\" + keyword)\n                self.fail(\"expected failure\")\n            except FormulaError, e:\n                self.assertEquals(str(e), \"Error in formula at position 1: '%s' is a reserved word\" % keyword)\n\n        self.assertCannotParse(\"=and\", -1)\n        AssertKeywordError(\"assert\")\n        AssertKeywordError(\"break\")\n        AssertKeywordError(\"class\")\n        AssertKeywordError(\"continue\")\n        AssertKeywordError(\"def\")\n        AssertKeywordError(\"del\")\n        AssertKeywordError(\"elif\")\n        AssertKeywordError(\"else\")\n        AssertKeywordError(\"except\")\n        AssertKeywordError(\"exec\")\n        AssertKeywordError(\"finally\")\n        self.assertCannotParse(\"=for\", 2, \"for\")\n        AssertKeywordError(\"from\")\n        AssertKeywordError(\"global\")\n        self.assertCannotParse(\"=if\", -1)\n        AssertKeywordError(\"import\")\n        self.assertCannotParse(\"=in\", 2, \"in\")\n        self.assertCannotParse(\"=is\", 2, \"is\")\n        self.assertCannotParse(\"=lambda\", -1)\n        self.assertCannotParse(\"=not\", -1)\n        self.assertCannotParse(\"=or\", -1)\n        AssertKeywordError(\"pass\")\n        AssertKeywordError(\"print\")\n        AssertKeywordError(\"raise\")\n        AssertKeywordError(\"return\")\n        AssertKeywordError(\"try\")\n        AssertKeywordError(\"while\")\n\n\n    def testCellRefLike(self):\n        \"test cell ref like name atttributes\"\n\n        self.assertParsesToFLReferenceChildren(\"=something.A1\", [\n            Atom([Name([\"something\"])]),\n            Trailer([\".\", Name([\"A1\"])])\n        ])\n        self.assertParsesToFLReferenceChildren(\"=something.AAAAAA\", [\n            Atom([Name([\"something\"])]),\n            Trailer([\".\", Name([\"AAAAAA\"])])\n        ])\n        self.assertParsesToFLReferenceChildren(\"=something.A1.Top2\", [\n            Atom([Name([\"something\"])]),\n            Trailer([\".\", Name([\"A1\"])]),\n            Trailer([\".\", Name([\"Top2\"])])\n         ])\n\n        self.assertParsesToFLReferenceChildren(\"=something.A1()\", [\n            Atom([Name([\"something\"])]),\n            Trailer([\".\", Name([\"A1\"])]),\n            Trailer([\"(\", ')'])\n        ])\n        self.assertParsesToFLReferenceChildren(\"=something.  A2\", [\n            Atom([Name([\"something\"])]),\n            Trailer([\".  \", Name([\"A2\"])])\n        ])\n        self.assertParsesToFLReferenceChildren(\"=something.\\tA3\", [\n            Atom([Name([\"something\"])]),\n            Trailer([\".\\t\", Name([\"A3\"])])\n        ])\n\n\n\n    def testMissingParametersIn(self):\n        \"test missing parameters in arg list\"\n        def TestFromName(name):\n            return TestFromAtomChild(Name([name]))\n        x = TestFromName('x')\n        empty = TestFromName('')\n        def CreateParseTree(argList):\n            return FLRoot([\"=\", CreateTest('foo', '(', argList,')')])\n\n        self.assertParsesToTree('=foo(,)', CreateParseTree([empty, ',']))\n        self.assertParsesToTree('=foo(, ,)', CreateParseTree([empty, ', ', empty, ',']))\n        self.assertParsesToTree('=foo(,, ,)', CreateParseTree([empty, ',', empty, ', ', empty, ',']))\n        self.assertParsesToTree('=foo(x,)', CreateParseTree([x, ',']))\n        self.assertParsesToTree('=foo(, x)', CreateParseTree([empty, ', ', x]))\n        self.assertParsesToTree('=foo(x, , x)', CreateParseTree([x, ', ', empty, ', ', x]))\n        self.assertParsesToTree('=foo(x, x, )', CreateParseTree([x, ', ', x, ', ']))\n        self.assertParsesToTree('=foo(, x, x)', CreateParseTree([empty, ', ', x, ', ', x]))\n        self.assertParsesToTree('=foo(, x, )', CreateParseTree([empty, ', ', x, ', ']))\n\n\n    def testMissingParametersIn2(self):\n        \"test missing parameters in arg list with args and kwargs\"\n        empty = Argument([TestFromAtomChild(Name(['']))])\n\n        def CreateParseTree(argListChildren):\n            return FLRoot([\"=\", Test_AndTest_NotTest_Comparison(\n                Expr_ConcatExpr_ShiftExpr(\n                        ArithExpr_Term(\n                            Factor(\n                                [Power(\n                                    [FLReference(\n                                        [\n                                        Atom([Name(['foo'])]),\n                                        Trailer(\n                                            [\n                                            '(',\n                                            ArgList(argListChildren),\n                                            ')'\n                                            ])\n                                        ])])]))))])\n\n\n\n        self.assertParsesToTree('=foo(, *x)', CreateParseTree([empty, ', ', '*', TestFromAtomChild(Name([\"x\"]))]))\n        self.assertParsesToTree('=foo(, **x)', CreateParseTree([empty, ', ', '**', TestFromAtomChild(Name([\"x\"]))]))\n        keyword = Argument([TestFromAtomChild(Name([\"c\"])),\n                            \":=\",\n                            TestFromAtomChild(Number([\"1\"]))\n                            ])\n        self.assertParsesToTree('=foo(, c:=1)', CreateParseTree([empty, ', ', keyword]))\n\n        self.assertCannotParse('=foo(*args, , )', 13, ', ')\n        self.assertCannotParse('=foo(bar, *args, **kwargs, , )', 26, ', ')\n        self.assertCannotParse('=foo(bar, *args, , **kwargs)', 18, ', ')\n\n\n\nclass ParserModuleTest(unittest.TestCase):\n\n    def test_reloading_module_should_not_regenerate_parsetab(self):\n        # NB the parser reads the file parsetab.py, but writes to the filename below.  This\n        # means that if you want it to not regenerate the parsetab every time the module is\n        # loaded, having updated the grammar since parsetab.py was written and thus made a\n        # regeneration necessary, you need to copy the written file on top of the one that is\n        # read.\n        parseTabWriteTime = os.path.getmtime(\"sheet/parser/parsetab.py\")\n\n        import sheet.parser.parser as ParserModule\n        reload(ParserModule)\n        self.assertEquals(os.path.getmtime(\"sheet/parser/parsetab.py\"),\n                          parseTabWriteTime,\n                          \"ParseTab was written to by reload\")\n\n    def test_multithreaded_parsing_works(self):\n        def hammer_parser(errors):\n            start = time.clock()\n            while time.clock() - start < 1:\n                try:\n                    parse(\"=a1 + a2 + a3\")\n                except Exception, e:\n                    errors.append(e)\n\n        thread_count = 2\n        threads = []\n        for t in range(thread_count):\n            errors = []\n            thread = Thread(target=lambda: hammer_parser(errors))\n            thread.errors = errors\n            thread.start()\n            threads.append(thread)\n\n        # Join to all of the threads but just raise the first error\n        first_error = None\n        for thread in threads:\n            thread.join()\n            if thread.errors:\n                first_error = thread.errors[1]\n        if first_error:\n            raise first_error\n"
  },
  {
    "path": "dirigible/sheet/tests/test_calculate.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\nfrom __future__ import with_statement\n\nfrom datetime import datetime\nfrom Queue import Queue\nimport sys\nfrom textwrap import dedent\nfrom unittest import SkipTest\nfrom urllib import urlencode\n\n\nfrom mock import call, Mock, patch, sentinel\n\nfrom dirigible.test_utils import die, ResolverTestCase\n\nimport sheet.calculate as calculate_module\nfrom sheet.calculate import (\n    api_json_to_worksheet, calculate, calculate_with_timeout,\n    create_cell_recalculator, CURRENT_API_VERSION, evaluate_formulae_in_context,\n    execute_usercode, format_traceback, is_nan, load_constants, _raise, recalculate_cell,\n    run_worksheet, MyStdout)\nfrom sheet.cell import Cell, undefined\nfrom sheet.dirigible_datetime import DateTime\nfrom sheet.models import Sheet, User\nfrom sheet.parser import FormulaError\nfrom sheet.views_api_0_1 import _sheet_to_value_only_json\nfrom sheet.worksheet import CellRange, Worksheet, worksheet_from_json, worksheet_to_json\n\n\n\nclass TestIsNan(ResolverTestCase):\n\n    def test_is_nan(self):\n        self.assertTrue(is_nan(float('nan')))\n        self.assertFalse(is_nan(23.3))\n        self.assertFalse(is_nan(23))\n\n    def test_is_nan_with_numpy(self):\n        # known to cause problems historically:\n        try:\n            import numpy\n            self.assertFalse(is_nan(numpy.array([1, 2, 3])))\n        except ImportError:\n            raise SkipTest('No numpy')\n\n\n\nclass TestLoadConstants(ResolverTestCase):\n\n    @patch('sheet.calculate.eval_constant')\n    def test_load_constants_should_call_eval_constant_only_on_every_constant_location(self, mock_eval_constant):\n        worksheet = Worksheet()\n        worksheet[11,1].formula = \"=formula1\"\n        worksheet[22,2].formula = \"constant\"\n        worksheet[33,3].formula = \"=formula2\"\n        worksheet[44,4].formula = \"another constant\"\n\n        load_constants(worksheet)\n        expected = [\n            ((\"constant\",), {}),\n            ((\"another constant\",), {}),\n        ]\n        self.assertItemsEqual(mock_eval_constant.call_args_list, expected)\n\n\n    def test_load_constants_should_clear_errors_for_constants(self):\n        cell = Cell()\n        cell.formula = \"a constant\"\n        cell.error = 'Ohno!'\n        worksheet = Worksheet()\n        worksheet.A1 = cell\n\n        load_constants(worksheet)\n\n        self.assertIsNone(cell.error)\n\n\n\nclass TestRecalculateCell(ResolverTestCase):\n\n    def test_recalculate_cell_evals_python_formula_in_context_and_puts_results_in_worksheet(self):\n        location = (1, 2)\n        cell = Cell()\n        cell.python_formula = '100 + fred'\n        context = { 'fred': 23, \"worksheet\": { location: cell } }\n\n        # Mocked out to avoid explosion -- tested in another unit test.\n        node = Mock()\n        node.parents = []\n\n        recalculate_cell(location, None, { location: node }, context)\n\n        self.assertEquals(cell.value, 123)\n\n\n    def test_recalculate_cell_should_perform_true_division(self):\n        location = (1, 2)\n        cell = Cell()\n        cell.python_formula = '1/4'\n        context = { \"worksheet\": { location: cell } }\n\n        # Mocked out to avoid explosion -- tested in another unit test.\n        node = Mock()\n        node.parents = []\n\n        recalculate_cell(location, None, { location: node }, context)\n\n        self.assertEquals(cell.value, 0.25)\n\n\n    def test_recalculate_cell_should_remove_nodes_from_dependency_graph(self):\n        location = (1, 2)\n        cell = Cell()\n        cell.formula = '=123'\n        leaf_queue = Queue()\n        parent = Mock()\n        parent_loc = (2, 3)\n        node = Mock()\n        node.parents = set([(parent_loc)])\n        graph = { location: node, parent_loc: parent }\n        context = { 'worksheet': { location: cell, } }\n\n        recalculate_cell(location, leaf_queue, graph, context)\n\n        self.assertEquals( node.remove_from_parents.call_args, (([parent], leaf_queue,), {}) )\n\n\n    def test_recalc_cell_catches_cell_errors_and_adds_them_to_console(self):\n        cell = Cell()\n        cell.formula = \"=123\"\n        cell.python_formula = '_raise(Exception(\"OMGWTFBBQ\"))'\n        cell.error = 'old error, just hanging around...'\n        worksheet = Worksheet()\n        location = (1, 11)\n        worksheet[location] = cell\n\n        # Mocked out to avoid explosion -- tested in another unit test.\n        node = Mock()\n        node.parents = []\n        graph = {location: node }\n\n        context = { 'worksheet': worksheet, \"_raise\": _raise }\n        worksheet.add_console_text = Mock()\n\n        recalculate_cell(location, Mock(), graph, context)\n\n        self.assertEqual(\n            worksheet[location].error,\n            'Exception: OMGWTFBBQ'\n        )\n\n        expected_error_text = \"Exception: OMGWTFBBQ\\n    Formula '%s' in A11\\n\" % (\n                cell.formula)\n\n        self.assertCalledOnce(worksheet.add_console_text, expected_error_text)\n\n        self.assertEquals(worksheet[location].value, undefined)\n\n\n    def test_recalc_cell_should_clear_cell_error_and_not_add_to_console_text_on_eval_succeeding(self):\n        cell = Cell()\n        cell.formula = '=123'\n        cell.error = 'old error, just hanging around...'\n        worksheet = Worksheet()\n        location = (1, 11)\n        worksheet[location] = cell\n\n        node = Mock()\n        node.parents = []\n        graph = {location: node }\n\n        context = { 'worksheet': { location: cell, } }\n\n        recalculate_cell(location, Mock(), graph, context)\n\n        self.assertEqual(\n            worksheet[location].error,\n            None\n        )\n        self.assertEqual(\n            worksheet[location].value,\n            123\n        )\n        self.assertEqual(\n            worksheet._console_text, ''\n        )\n\n\nclass TestCreateCellRecalculator(ResolverTestCase):\n\n    @patch('sheet.calculate.recalculate_cell')\n    def test_create_cell_recalculator_should(self, mock_recalculate):\n        unrecalculated_queue = Queue()\n        unrecalculated_queue.put(1)\n        unrecalculated_queue.put(1)\n        unrecalculated_queue.task_done = Mock()\n\n        leaf_queue = Queue()\n        leaf_queue.put(sentinel.one)\n        leaf_queue.put(sentinel.two)\n        leaf_queue.task_done = Mock()\n\n        target = create_cell_recalculator(leaf_queue, unrecalculated_queue, sentinel.graph, sentinel.context)\n        target()\n\n        self.assertTrue(unrecalculated_queue.empty())\n        self.assertEquals(\n            unrecalculated_queue.task_done.call_args_list,\n            [ ((), {}), ((), {}), ]\n        )\n\n        self.assertTrue(leaf_queue.empty())\n\n        self.assertEquals(\n            mock_recalculate.call_args_list,\n            [\n                ((sentinel.one, leaf_queue, sentinel.graph, sentinel.context), {}),\n                ((sentinel.two, leaf_queue, sentinel.graph, sentinel.context), {})\n            ]\n        )\n\n        self.assertEquals(\n            leaf_queue.task_done.call_args_list,\n            [ ((), {}), ((), {}), ]\n        )\n\n    @patch('sheet.calculate.recalculate_cell')\n    def test_create_cell_recalculator_should_handle_exceptions_from_recalc_cell(self, mock_recalculate):\n        mock_recalculate.side_effect = die()\n        unrecalculated_queue = Queue()\n        unrecalculated_queue.put(1)\n        unrecalculated_queue.task_done = Mock()\n\n        leaf_queue = Queue()\n        leaf_queue.put(sentinel.one)\n        leaf_queue.task_done = Mock()\n\n        target = create_cell_recalculator(leaf_queue, unrecalculated_queue, sentinel.graph, sentinel.context)\n        self.assertRaises(AssertionError, target)\n\n        self.assertTrue(unrecalculated_queue.empty())\n        self.assertCalledOnce(unrecalculated_queue.task_done)\n        self.assertTrue(leaf_queue.empty())\n        self.assertCalledOnce(mock_recalculate, sentinel.one, leaf_queue, sentinel.graph, sentinel.context)\n        self.assertCalledOnce(leaf_queue.task_done)\n\n\nclass TestEvaluateFormulaeInContext(ResolverTestCase):\n\n    @patch('sheet.calculate.NUM_THREADS', 2)\n    @patch('sheet.calculate.build_dependency_graph')\n    @patch('sheet.calculate.Thread')\n    @patch('sheet.calculate.Queue')\n    @patch('sheet.calculate.create_cell_recalculator')\n    def test_evaluate_formulae_in_context_builds_dependency_graph_and_recalculates_it_on_threads(\n        self, mock_create_cell_recalculator, mock_queue_class, mock_thread_class,\n        mock_build_dependency_graph\n    ):\n        mock_thread = mock_thread_class.return_value\n        mock_queue_class.side_effect = lambda: Mock()\n        mock_create_cell_recalculator.return_value = sentinel.create_cell_recalculator\n        graph = {\n            (1, 1): set([(1, 2), (1, 3)]),\n            (1, 3): set([(2, 2), (2, 3)]),\n            (2, 2): set(),\n            (1, 2): set(),\n            (2, 3): set()\n        }\n        leaves = [(2, 2), (1, 2), (2, 3)]\n        mock_build_dependency_graph.return_value = ( graph, leaves )\n\n        evaluate_formulae_in_context(Mock(), Mock())\n\n        arg_ids = [map(id, x[0]) for x in mock_create_cell_recalculator.call_args_list]\n        self.assertEquals(arg_ids[0][0], arg_ids[1][0], 'leaf queues passed into threads not the same')\n        self.assertEquals(arg_ids[0][1], arg_ids[1][1], 'completed queues passed into threads not the same')\n        self.assertEquals(\n            [args[0][2] for args in mock_create_cell_recalculator.call_args_list],\n            [graph, graph]\n        )\n        mock_leaf_queue = mock_create_cell_recalculator.call_args_list[0][0][0]\n        self.assertEquals(\n            mock_leaf_queue.put.call_args_list,\n            [\n               ((leaves[0],), {}),\n               ((leaves[1],), {}),\n               ((leaves[2],), {})\n            ])\n\n        unrecalculated_cell_queue = mock_create_cell_recalculator.call_args_list[0][0][1]\n        self.assertEquals(len(unrecalculated_cell_queue.put.call_args_list), len(graph), 'unrecalculated queue not correctly populated')\n\n        self.assertEquals(mock_thread_class.call_args_list,\n            [\n                ((), {'target': sentinel.create_cell_recalculator}),\n                ((), {'target': sentinel.create_cell_recalculator}),\n            ]\n        )\n\n        self.assertEquals(mock_thread.setDaemon.call_args_list,\n            [\n                ((True, ), {}),\n                ((True, ), {}),\n            ]\n        )\n\n        self.assertEquals(mock_thread.start.call_args_list,\n            [\n                ((), {}),\n                ((), {}),\n            ]\n        )\n\n        self.assertCalledOnce(unrecalculated_cell_queue.join)\n\n\nclass TestExecuteUsercode(ResolverTestCase):\n\n    def test_execute_usercode_does(self):\n        context = {}\n        execute_usercode('x = \"test\"', context)\n        self.assertEquals(context['x'], 'test')\n\n\nSANITY_CHECK_USERCODE = dedent(\"\"\"\n    load_constants(worksheet)\n    %s\n    evaluate_formulae(worksheet)\n    %s\n\"\"\")\nclass TestCalculate(ResolverTestCase):\n\n    @patch('sheet.calculate.execute_usercode')\n    @patch('sheet.calculate.evaluate_formulae_in_context')\n    def test_calculate_should_execute_usercode_with_correct_context_and_curried_evaluate_formulae_in_context(\n        self, mock_evaluate_formulae_in_context, mock_execute_usercode\n    ):\n        worksheet = Worksheet()\n        calculate(worksheet, sentinel.usercode, sentinel.private_key)\n\n        args, kwargs = mock_execute_usercode.call_args\n\n        self.assertEquals(kwargs, {})\n\n        self.assertEquals(args[0], sentinel.usercode)\n\n        context = args[1]\n        self.assertEquals(context['CellRange'], CellRange)\n        self.assertEquals(context['DateTime'], DateTime)\n        self.assertEquals(context['FormulaError'], FormulaError)\n        self.assertEquals(context['_raise'], _raise)\n        self.assertEquals(context['sys'], sys)\n\n        self.assertEquals(context['worksheet'], worksheet)\n        self.assertEquals(context['load_constants'], load_constants)\n        self.assertEquals(context['undefined'], undefined)\n        evaluate_formulae = context['evaluate_formulae']\n        evaluate_formulae(sentinel.worksheet)\n        self.assertEquals(\n            mock_evaluate_formulae_in_context.call_args,\n            ((sentinel.worksheet, context), {})\n        )\n\n    @patch('sheet.calculate.execute_usercode')\n    def test_calculate_patches_sys_stdout_in_context(\n        self, mock_execute_usercode\n    ):\n        worksheet = Worksheet()\n\n        def check_stdout(_, context):\n            self.assertEquals(type(context['sys'].stdout), MyStdout)\n            self.assertEquals(context['sys'].stdout.worksheet, worksheet)\n        mock_execute_usercode.side_effect = check_stdout\n\n        calculate(worksheet, sentinel.usercode, sentinel.private_key)\n\n        self.assertNotEqual(type(sys.stdout), MyStdout)\n\n\n    def test_mystdout_pushes_print_commands_to_worksheet(self):\n        ws = Worksheet()\n        mso = MyStdout(ws)\n        mso.write('weeeeeee!')\n        ws2 = Worksheet()\n        ws2.add_console_text('weeeeeee!', log_type='output')\n        self.assertEquals(ws._console_text, ws2._console_text)\n\n\n    @patch('sheet.calculate.execute_usercode')\n    @patch('sheet.calculate.run_worksheet')\n    def test_calculate_puts_curried_run_worksheet_into_context(self, mock_run_worksheet, mock_execute_usercode):\n        worksheet = Worksheet()\n        calculate(worksheet, sentinel.usercode, sentinel.private_key)\n        args, kwargs = mock_execute_usercode.call_args\n        context = args[1]\n        curried_run_worksheet = context['run_worksheet']\n        self.assertEquals(mock_run_worksheet.call_args_list, [])\n        curried_run_worksheet(sentinel.urls)\n        self.assertCalledOnce(mock_run_worksheet, sentinel.urls, None, sentinel.private_key)\n\n        mock_run_worksheet.reset_mock()\n        curried_run_worksheet(sentinel.urls, sentinel.overrides)\n        self.assertCalledOnce(mock_run_worksheet, sentinel.urls, sentinel.overrides, sentinel.private_key)\n\n\n    @patch('sheet.calculate.execute_usercode')\n    def test_calculate_clears_previous_worksheet_cell_values_before_executing_usercode(\n        self, mock_execute_usercode\n    ):\n        calls_list = []\n        worksheet = Worksheet()\n        worksheet.clear_values = Mock()\n\n        worksheet.clear_values.side_effect = lambda *args : calls_list.append('clear values')\n        mock_execute_usercode.side_effect  = lambda *args : calls_list.append('execute usercode')\n\n        calculate(worksheet, sentinel.usercode, sentinel.private_key)\n\n        self.assertEquals(calls_list, ['clear values', 'execute usercode'])\n\n\n    @patch('sheet.calculate.execute_usercode', Mock())\n    @patch('sheet.calculate.evaluate_formulae_in_context', Mock())\n    @patch('sheet.calculate.time')\n    def test_calculate_clears_previous_worksheet_console_text_and_reports_time(self, mock_time):\n        recalc_times = [1.3245, 0]\n        def mock_time_fn():\n            return recalc_times.pop()\n        mock_time.side_effect = mock_time_fn\n        worksheet = Worksheet()\n        worksheet._console_text = 'previous errors'\n        worksheet.add_console_text = Mock()\n\n        calculate(worksheet, sentinel.usercode, sentinel.private_key)\n        expected_text = 'Took 1.32s'\n        self.assertEquals(worksheet.add_console_text.call_args_list[0],\n                          ((expected_text,),{'log_type':'system'})\n        )\n\n\n    @patch('sheet.calculate.evaluate_formulae_in_context', Mock())\n    @patch('sheet.calculate.execute_usercode')\n    @patch('sheet.calculate.time')\n    def test_calculate_clears_previous_worksheet_console_text_and_reports_time_when_theres_an_error(self, mock_time, mock_execute_usercode):\n        recalc_times = [1.3245, 0]\n        def mock_time_fn():\n            return recalc_times.pop()\n        mock_time.side_effect = mock_time_fn\n        def throw_error(_, __):\n            raise Exception('argh')\n        mock_execute_usercode.side_effect = throw_error\n        worksheet = Worksheet()\n        worksheet._console_text = 'previous errors\\n'\n        worksheet.add_console_text = Mock()\n\n        calculate(worksheet, sentinel.usercode, sentinel.private_key)\n\n        self.assertNotIn('previous errors', worksheet._console_text)\n        self.assertEquals(\n            worksheet.add_console_text.call_args_list[-1],\n            (('Took 1.32s',),{'log_type':'system'}),\n        )\n\n    @patch('sheet.calculate.execute_usercode')\n    @patch('sheet.calculate.evaluate_formulae_in_context')\n    def test_calculate_clears_previous_worksheet_usercode_error(self, mock_evaluate_formulae_in_context, mock_execute_usercode):\n        worksheet = Worksheet()\n        worksheet._usercode_error = \"Argh!\"\n        calculate(worksheet, sentinel.usercode, sentinel.private_key)\n        self.assertEquals(worksheet._usercode_error, None)\n\n\n\n    @patch('sheet.calculate.execute_usercode')\n    @patch('sheet.calculate.evaluate_formulae_in_context', Mock())\n    def test_calculate_catches_usercode_exceptions(self, mock_execute_usercode):\n        worksheet = Worksheet()\n        def execute_usercode(_, __):\n            exec('1/0\\n')\n        mock_execute_usercode.side_effect = execute_usercode\n        try:\n            calculate(worksheet, sentinel.usercode, sentinel.private_key)\n        except:\n            self.fail(\"Unhandled exception when executing broken usercode\")\n\n\n    @patch('sheet.calculate.execute_usercode')\n    @patch('sheet.calculate.evaluate_formulae_in_context')\n    def test_calculate_catches_and_reports_exceptions_in_worksheet_usercode_error_field(self, mock_evaluate_formulae_in_context, mock_execute_usercode):\n        worksheet = Worksheet()\n        def execute_usercode(_, __):\n            exec(\n                'import sys\\n'\n                'def func():\\n'\n                '    x = my_value\\n'\n                'func()\\n'\n            )\n        mock_execute_usercode.side_effect = execute_usercode\n        calculate(worksheet, sentinel.usercode, sentinel.private_key)\n        expected = {\n            'message': 'NameError: global name \\'my_value\\' is not defined',\n            'line': 3,\n        }\n        self.assertEquals(worksheet._usercode_error, expected)\n\n\n    def test_calculate_catches_and_reports_syntax_errors_with_special_message_in_worksheet_usercode_error_field(self):\n\n        def patched_execute_usercode(_, __):\n            exec('import sys:\\nx == my_value')\n        worksheet = Worksheet()\n\n        original_execute_usercode = calculate_module.execute_usercode\n        calculate_module.execute_usercode = patched_execute_usercode\n        try:\n            calculate(worksheet, sentinel.usercode, sentinel.private_key)\n        finally:\n            calculate_module.execute_usercode = original_execute_usercode\n\n        expected = {\n            'message': 'Syntax error at character 11',\n            'line': 1\n        }\n        self.assertEquals(worksheet._usercode_error, expected)\n\n\n    def test_calculate_catches_and_reports_exceptions_to_console(self):\n\n        def patched_execute_usercode(_, context):\n            exec(\n                'import sys\\n'\n                'def func():\\n'\n                '    x = my_value\\n'\n                'func()\\n',\n                context\n            )\n        worksheet = Worksheet()\n        worksheet.add_console_text = Mock()\n        original_execute_usercode = calculate_module.execute_usercode\n        calculate_module.execute_usercode = patched_execute_usercode\n        try:\n            calculate(worksheet, sentinel.usercode, sentinel.private_key)\n        finally:\n            calculate_module.execute_usercode = original_execute_usercode\n\n        expected_error_text = dedent(\"\"\"\n                    NameError: global name \\'my_value\\' is not defined\n                        User code line 4\n                        User code line 3, in func\\n\"\"\")[1:]\n        self.assertIn(\n            call(expected_error_text),\n            worksheet.add_console_text.call_args_list\n        )\n\n\n    @patch('sheet.calculate.execute_usercode')\n    @patch('sheet.calculate.evaluate_formulae_in_context')\n    def test_calculate_catches_and_reports_syntax_errors_to_console(self, mock_evaluate_formulae_in_context, mock_execute_usercode):\n        worksheet = Worksheet()\n        worksheet.add_console_text = Mock()\n\n        def execute_usercode(_, __):\n            exec('import sys:\\nx == my_value')\n        mock_execute_usercode.side_effect = execute_usercode\n        calculate(worksheet, sentinel.usercode, sentinel.private_key)\n\n        expected_error_text = 'Syntax error at character 11 (line 1)\\n'\n        self.assertIn(\n            ((expected_error_text,), {}),\n            worksheet.add_console_text.call_args_list\n        )\n\n\n    def test_format_traceback_filters_frames_that_are_dirigible_code(self):\n        import sheet.worksheet as worksheet_module\n\n        frames = [\n            (calculate_module.__file__, 83,\n                'calculate', 'execute_usercode(usercode, context)'),\n            (calculate_module.__file__, 71,\n                'execute_usercode', 'exec(usercode, context)'),\n            (\"<string>\", 14,\n                '<module>', None),\n            (\"<string>\", 4,\n                'fn', None),\n            (\"build/bdist.linux-i686/egg/simplejson/__init__.py\", 384,\n                'loads', None),\n            (\"build/bdist.linux-i686/egg/simplejson/decoder.py\", 402,\n                'decode', 'obj, end = self.raw_decode(s, idx=_w(s, 0).end())'),\n            (worksheet_module.__file__, 25,\n                \"__setitem__\", 'raise TypeError(\"Worksheet locations must be Cell objects\")'),\n        ]\n\n        expected = '''\n    User code line 14\n    User code line 4, in fn\n    File \"build/bdist.linux-i686/egg/simplejson/__init__.py\" line 384, in loads\n    File \"build/bdist.linux-i686/egg/simplejson/decoder.py\" line 402, in decode\n        obj, end = self.raw_decode(s, idx=_w(s, 0).end())'''[1:]\n\n        self.assertEquals(format_traceback(frames), expected)\n\n\nclass TestCalculateWithTimeout(ResolverTestCase):\n\n    @patch('sheet.calculate.calculate')\n    def test_calculate_with_timeout_calls_calculate_function_with_contents_and_usercode(\n        self, mock_calculate\n    ):\n        long_timeout_seconds = 100\n        calculate_with_timeout(\n            sentinel.worksheet, sentinel.usercode,\n            long_timeout_seconds, sentinel.private_key\n        )\n\n        self.assertEquals(\n            mock_calculate.call_args,\n            ((sentinel.worksheet, sentinel.usercode, sentinel.private_key), {})\n        )\n\n\n    @patch('sheet.calculate.InterruptableThread')\n    def test_calculate_with_timeout_uses_interruptable_thread_with_correct_timeout(\n        self, mock_ithread_class\n    ):\n        mock_ithread_class.return_value = mock_ithread = Mock()\n        mock_ithread.isAlive.return_value = False\n        calculate_with_timeout(\n            sentinel.worksheet, sentinel.usercode,\n            sentinel.timeout_seconds, sentinel.private_key\n        )\n        self.assertEquals(mock_ithread.method_calls,\n                          [\n                              ('start', (), {}),\n                              ('join', (sentinel.timeout_seconds,), {}),\n                              ('isAlive', (), {}),\n                              ]\n                          )\n\n\n    @patch('sheet.calculate.InterruptableThread')\n    @patch('sheet.calculate.sleep')\n    def test_calculate_with_timeout_tries_to_interrupt_timed_out_thread(\n        self, mock_sleep, mock_ithread_class\n    ):\n        def check_interrupt_called_first(_):\n            self.assertTrue(mock_ithread.interrupt.called)\n        mock_sleep.side_effect = check_interrupt_called_first\n\n        mock_ithread_class.return_value = mock_ithread = Mock()\n        def set_is_alive():\n            mock_ithread.alive = not mock_ithread.alive\n            return not mock_ithread.alive\n        mock_ithread.isAlive.side_effect = set_is_alive\n\n        calculate_with_timeout(\n            sentinel.worksheet, sentinel.usercode,\n            sentinel.timeout_seconds, sentinel.private_key\n        )\n        self.assertEquals(mock_ithread.method_calls,\n                          [\n                              ('start', (), {}),\n                              ('join', (sentinel.timeout_seconds,), {}),\n                              ('isAlive', (), {}),\n                              ('interrupt', (), {}),\n                              ('isAlive', (), {}),\n                              ]\n                          )\n        self.assertEquals(mock_sleep.call_args, ((0.1,), {}))\n\n\n\nclass TestRaise(ResolverTestCase):\n\n    def test_raise_raises(self):\n        exception = Exception(\"Argh\")\n        with self.assertRaises(Exception) as mgr:\n            _raise(exception)\n        self.assertEquals(mgr.exception, exception)\n\n\n\nclass TestRunWorksheet(ResolverTestCase):\n\n    @patch('sheet.calculate.urllib2')\n    @patch('sheet.calculate.api_json_to_worksheet')\n    def test_run_worksheet_no_overrides(self, mock_api_json_to_worksheet, mock_urllib2):\n        mock_api_json_to_worksheet.return_value = Worksheet()\n        worksheet_url = 'ws_url/'\n        target_url = '%sv%s/json/' % (worksheet_url, CURRENT_API_VERSION)\n        result = run_worksheet(worksheet_url, None, sentinel.private_key)\n\n        self.assertCalledOnce(mock_urllib2.build_opener)\n        mock_opener = mock_urllib2.build_opener.return_value\n\n        self.assertCalledOnce(mock_opener.open, target_url, data=urlencode({'dirigible_l337_private_key': sentinel.private_key}))\n        mock_urlopen_file = mock_opener.open.return_value\n\n        self.assertCalledOnce(mock_api_json_to_worksheet, mock_urlopen_file.read.return_value)\n        self.assertEquals(result, mock_api_json_to_worksheet.return_value)\n\n\n    @patch('sheet.calculate.urlencode')\n    @patch('sheet.calculate.urllib2')\n    @patch('sheet.calculate.api_json_to_worksheet')\n    def test_run_worksheet_with_overrides(self, mock_api_json_to_worksheet, mock_urllib2, mock_urlencode):\n        overrides = {'a1': 55}\n        str_overrides = {'a1': '55', 'dirigible_l337_private_key': sentinel.private_key}\n        mock_api_json_to_worksheet.return_value = Worksheet()\n        worksheet_url = 'ws_url/'\n        target_url = '%sv%s/json/' % (worksheet_url, CURRENT_API_VERSION)\n        result = run_worksheet(worksheet_url, overrides, sentinel.private_key)\n\n        self.assertCalledOnce(mock_urllib2.build_opener)\n        mock_opener = mock_urllib2.build_opener.return_value\n\n        self.assertCalledOnce(mock_urlencode, str_overrides)\n        encoded_overrides = mock_urlencode.return_value\n\n        self.assertCalledOnce(mock_opener.open, target_url, data=encoded_overrides)\n        mock_urlopen_file = mock_opener.open.return_value\n\n        self.assertCalledOnce(mock_api_json_to_worksheet, mock_urlopen_file.read.return_value)\n        self.assertEquals(result, mock_api_json_to_worksheet.return_value)\n\n\n    @patch('sheet.calculate.urllib2')\n    def test_run_worksheet_with_error(self, mock_urllib2):\n        mock_opener = mock_urllib2.build_opener.return_value\n        mock_urlopen_file = mock_opener.open.return_value\n        mock_urlopen_file.read.return_value = '{ \"usercode_error\" : { \"message\": \"error\", \"line\": \"line_no\" } }'\n\n        with self.assertRaises(Exception) as mngr:\n            run_worksheet('worksheet_url', None, sentinel.private_key)\n        self.assertEquals(str(mngr.exception), 'run_worksheet: error')\n\n\n    @patch('sheet.calculate.urllib2')\n    @patch('sheet.calculate.api_json_to_worksheet')\n    def test_run_worksheet_passes_private_key_in_params(self, mock_api_json_to_worksheet, mock_urllib2):\n        worksheet_url = 'ws_url/'\n        target_url = '%sv%s/json/' % (worksheet_url, CURRENT_API_VERSION)\n        mock_api_json_to_worksheet.return_value = Worksheet()\n\n        mock_opener = mock_urllib2.build_opener.return_value\n        mock_urlopen_file = mock_opener.open.return_value\n        mock_urlopen_file.read.return_value = sentinel.worksheet_json\n\n        run_worksheet(worksheet_url, None, sentinel.private_key)\n\n        self.assertCalledOnce(mock_opener.open, target_url, data=urlencode({'dirigible_l337_private_key': sentinel.private_key}))\n\n\n\n\nclass TestJsonToWorksheet(ResolverTestCase):\n\n    def test_values(self):\n        json = u'''\n        {\n            \"name\": \"sheetname\",\n            \"1\": {\n                \"2\": \"abc\",\n                \"3\": \"123\",\n                \"4\": 123,\n                \"5\": [1, 2, 3, 4],\n                \"6\": \"unescaped & unicod\\xe9\"\n            }\n        }\n        '''\n        actual = api_json_to_worksheet(json)\n        expected = Worksheet()\n        expected.name = 'sheetname'\n        expected[1, 2].value = 'abc'\n        expected[1, 3].value = '123'\n        expected[1, 4].value = 123\n        expected[1, 5].value = [1, 2, 3, 4]\n        expected[1, 6].value = u'unescaped & unicod\\xe9'\n\n        self.assertEquals(dict(actual), dict(expected))\n        self.assertEquals(actual, expected)\n\n\n    def test_should_call_worksheets_without_name_untitled(self):\n        json = '{\"something\": \"else\"}'\n        try:\n            result = api_json_to_worksheet(json)\n        except KeyError:\n            self.fail('shouldnt barf on worksheet with no name')\n        self.assertEquals(result.name, 'Untitled')\n\n\n    def test_should_copy_errors_across(self):\n        json = '''\n            {\n                \"usercode_error\" : {\"message\": \"ohno!\", \"something\": \"else\"}\n            }\n        '''\n        try:\n            result = api_json_to_worksheet(json)\n        except KeyError:\n            self.fail('shouldnt barf on worksheet with error')\n        self.assertTrue(isinstance(result, Worksheet))\n        self.assertEquals(\n            result._usercode_error,\n            {\"message\": \"ohno!\", \"something\": \"else\"}\n        )\n\n\n\nclass TestCalculateSemiFunctional(ResolverTestCase):\n\n    def test_totally_empty(self):\n        worksheet = Worksheet()\n        calculate(worksheet, '', sentinel.private_key)\n        self.assertEquals(worksheet, Worksheet())\n\n\n    def test_empty_worksheet(self):\n        worksheet = Worksheet()\n        calculate(worksheet, SANITY_CHECK_USERCODE % ('', ''), sentinel.private_key)\n        self.assertEquals(worksheet, Worksheet())\n\n\n    def test_constant(self):\n        worksheet = Worksheet()\n        worksheet[1, 2].formula = '3'\n        calculate(worksheet, SANITY_CHECK_USERCODE % ('', ''), sentinel.private_key)\n        self.assertEquals(worksheet.keys(), [(1, 2)])\n        self.assertEquals(worksheet[(1, 2)].formula, '3')\n        self.assertEquals(worksheet[(1, 2)].value, 3)\n\n\n    def test_multiple_constants(self):\n        worksheet = Worksheet()\n        worksheet[1, 2].formula = '3'\n        worksheet[3, 4].formula = '4+5'\n        worksheet[2, 5].formula = 'blurgle'\n\n        calculate(worksheet, SANITY_CHECK_USERCODE % ('', ''), sentinel.private_key)\n        self.assertEquals(set(worksheet.keys()), set([(1, 2), (3, 4), (2, 5)]))\n        self.assertEquals(worksheet[1, 2].formula, '3')\n        self.assertEquals(worksheet[3, 4].formula, '4+5')\n        self.assertEquals(worksheet[2, 5].formula, 'blurgle')\n        self.assertEquals(worksheet[1, 2].value, 3)\n        self.assertEquals(worksheet[3, 4].value, '4+5')\n        self.assertEquals(worksheet[2, 5].value, 'blurgle')\n\n\n    def test_arithmetic(self):\n        worksheet = Worksheet()\n        worksheet[1, 2].formula = '=3'\n        worksheet[3, 4].formula = '=4+5'\n        worksheet[4, 4].formula = '=1/10'\n\n        calculate(worksheet, SANITY_CHECK_USERCODE % ('', ''), sentinel.private_key)\n\n        self.assertEquals(set(worksheet.keys()), set([(1, 2), (3, 4), (4, 4)]))\n        self.assertEquals(worksheet[1, 2].value, 3)\n        self.assertEquals(worksheet[3, 4].value, 9)\n        self.assertEquals(worksheet[4, 4].value, 0.1)\n        self.assertEquals(worksheet[1, 2].formula, '=3')\n        self.assertEquals(worksheet[3, 4].formula, '=4+5')\n        self.assertEquals(worksheet[4, 4].formula, '=1/10')\n\n\n    def test_formulae(self):\n        worksheet = Worksheet()\n        worksheet[1, 1].formula = '=1'\n        worksheet[1, 2].formula = '=2'\n        worksheet[1, 3].formula = '=A1 + A2'\n        worksheet[1, 4].formula = '=DateTime(2000, 1, 2, 3, 4, 5)'\n\n        calculate(worksheet, SANITY_CHECK_USERCODE % ('', ''), sentinel.private_key)\n\n        self.assertEquals(set(worksheet.keys()), set([(1, 1), (1, 2), (1, 3), (1, 4)]))\n        self.assertEquals(worksheet[1, 1].value, 1)\n        self.assertEquals(worksheet[1, 2].value, 2)\n        self.assertEquals(worksheet[1, 3].value, 3)\n        self.assertEquals(worksheet[1, 4].value, datetime(2000, 1, 2, 3, 4, 5))\n        self.assertEquals(worksheet[1, 1].formula, '=1')\n        self.assertEquals(worksheet[1, 2].formula, '=2')\n        self.assertEquals(worksheet[1, 3].formula, '=A1 + A2')\n        self.assertEquals(worksheet[1, 4].formula, '=DateTime(2000, 1, 2, 3, 4, 5)')\n\n\n    def test_python_formulae(self):\n        worksheet = Worksheet()\n        worksheet[1, 1].python_formula = '1'\n        worksheet[1, 2].python_formula = '2'\n        worksheet[1, 3].python_formula = '1 + 2'\n\n        calculate(worksheet, SANITY_CHECK_USERCODE % ('', ''), sentinel.private_key)\n\n        self.assertEquals(set(worksheet.keys()), set([(1, 1), (1, 2), (1, 3)]))\n        self.assertEquals(worksheet[1, 1].value, 1)\n        self.assertEquals(worksheet[1, 2].value, 2)\n        self.assertEquals(worksheet[1, 3].value, 3)\n\n\n    def test_preformula_usercode(self):\n        worksheet = Worksheet()\n        worksheet[1, 1].formula = '3'\n        worksheet[1, 3].formula = '=A2 + 1'\n\n        preformula_usercode = \"worksheet[1, 2].value = worksheet[1, 1].value + 1\"\n        calculate(worksheet, SANITY_CHECK_USERCODE % (preformula_usercode, ''), sentinel.private_key)\n\n        self.assertEquals(set(worksheet.keys()), set([(1, 1), (1, 2), (1, 3)]))\n        self.assertEquals(worksheet[1, 1].value, 3)\n        self.assertEquals(worksheet[1, 2].value, 4)\n        self.assertEquals(worksheet[1, 3].value, 5)\n        self.assertEquals(worksheet[1, 1].formula, '3')\n        self.assertEquals(worksheet[1, 2].formula, None)\n        self.assertEquals(worksheet[1, 3].formula, '=A2 + 1')\n\n\n    def test_postformula_usercode(self):\n        worksheet = Worksheet()\n        worksheet[1, 1].formula = '=1'\n        postformula_usercode = 'worksheet[1, 2].value = worksheet[1, 1].value + 1'\n\n        calculate(worksheet, SANITY_CHECK_USERCODE % ('', postformula_usercode), sentinel.private_key)\n\n        self.assertEquals(set(worksheet.keys()), set([(1, 1), (1, 2)]))\n        self.assertEquals(worksheet[1, 1].value, 1)\n        self.assertEquals(worksheet[1, 2].value, 2)\n        self.assertEquals(worksheet[1, 1].formula, '=1')\n        self.assertEquals(worksheet[1, 2].formula, None)\n\n\n    def test_preformula_usercode_functions(self):\n        worksheet = Worksheet()\n        worksheet[1, 1].formula = '1'\n        worksheet[1, 2].formula = '=foo(3)'\n\n        preformula_usercode = dedent('''\n        def foo(value):\n            return worksheet[1, 1].value + value\n        ''')\n\n        calculate(worksheet, SANITY_CHECK_USERCODE % (preformula_usercode, ''), sentinel.private_key)\n\n        self.assertEquals(set(worksheet.keys()), set([(1, 1), (1, 2)]))\n        self.assertEquals(worksheet[1, 1].value, 1)\n        self.assertEquals(worksheet[1, 2].value, 4)\n        self.assertEquals(worksheet[1, 1].formula, '1')\n        self.assertEquals(worksheet[1, 2].formula, '=foo(3)')\n\n\n    @patch('sheet.calculate.urllib2')\n    def test_run_worksheet_should_return_worksheet_with_calculated_values_only(self, mock_urllib2):\n        self.maxDiff = None\n\n        original_sheet = Worksheet()\n\n        original_sheet.A2.formula = '1'\n        original_sheet.A2.value = 1\n\n        original_sheet.C3.formula = '5'\n        original_sheet.C3.value = 5\n\n        original_sheet.E4.formula = '=A2 + C3'\n        original_sheet.E4.value = 6\n\n        expected_sheet = Worksheet()\n        expected_sheet.name = 'Untitled'\n        for (col, row), cell in original_sheet.items():\n            expected_sheet[col, row].value = cell.value\n        foreign_sheet = Sheet()\n        foreign_sheet.owner = User(username='skeletor', password='1havTehpowa')\n        foreign_sheet.owner.save()\n        foreign_sheet.contents_json = worksheet_to_json(original_sheet)\n        foreign_sheet.calculate()\n\n        mock_opener = mock_urllib2.build_opener.return_value\n        mock_urlopen_file = mock_opener.open.return_value\n        mock_urlopen_file.read.return_value = _sheet_to_value_only_json(\n            foreign_sheet.name, worksheet_from_json(foreign_sheet.contents_json)\n        )\n\n        worksheet_url = 'ws_url/'\n        result = run_worksheet(worksheet_url, None, sentinel.private_key)\n\n        target_url = '%sv%s/json/' % (worksheet_url, CURRENT_API_VERSION)\n        self.assertCalledOnce(mock_opener.open, target_url, data=urlencode({'dirigible_l337_private_key': sentinel.private_key}))\n        self.assertEquals(type(result), Worksheet)\n        self.assertEquals(result, expected_sheet)\n\n\n    @patch('sheet.calculate.urllib2')\n    def test_run_worksheet_with_overrides(self, mock_urllib2):\n        self.maxDiff = None\n\n        cellA2 = Cell()\n        cellA2.formula = '1'\n        cellA2.value = 1\n\n        cellC3 = Cell()\n        cellC3.formula = '5'\n        cellC3.value = 5\n\n        cellE4 = Cell()\n        cellE4.formula = '=A2 + C3'\n        cellE4.value = 6\n\n        overrides = {\n            (1, 2): '11',\n            (3, 3): 55,\n            (4, 1): '=\"abc\"',\n            'dirigible_l337_private_key': sentinel.private_key\n        }\n\n        result_of_calculation_json = '''{\n            \"name\": \"Untitled\",\n            \"1\": {\n                \"2\": 11\n            },\n            \"3\": {\n                \"3\": 55\n            },\n            \"4\": {\n                \"1\": \"abc\"\n            },\n            \"5\": {\n                \"4\": 66\n            }\n        }'''\n\n        mock_opener = mock_urllib2.build_opener.return_value\n        mock_urlopen_file = mock_opener.open.return_value\n        mock_urlopen_file.read.return_value = result_of_calculation_json\n\n        worksheet_url = 'ws_url/'\n        result = run_worksheet(worksheet_url, overrides, sentinel.private_key)\n\n        target_url = '%sv%s/json/' % (worksheet_url, CURRENT_API_VERSION)\n        self.assertCalledOnce(mock_opener.open, target_url, data=urlencode(overrides))\n        self.assertEquals(type(result), Worksheet)\n        expected_sheet = Worksheet()\n        expected_sheet.name = 'Untitled'\n        expected_sheet[1, 2].value = 11\n        expected_sheet[3, 3].value = 55\n        expected_sheet[4, 1].value = 'abc'\n        expected_sheet[5, 4].value = 66\n\n        self.assertEquals(result, expected_sheet)\n"
  },
  {
    "path": "dirigible/sheet/tests/test_cell.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\nfrom __future__ import with_statement\n\ntry:\n    import unittest2 as unittest\nexcept ImportError:\n    import unittest\nfrom mock import Mock, patch, sentinel\n\nfrom sheet.cell import Cell, undefined\nfrom sheet.worksheet import Worksheet\nfrom dirigible.test_utils import ResolverTestCase\n\n\nclass TestUndefined(unittest.TestCase):\n\n    def test_repr(self):\n        self.assertEquals(repr(undefined), \"<undefined>\")\n\n\n\nclass TestCell(ResolverTestCase):\n\n    def test_initialisation(self):\n        self.assertEquals(Cell().formula, None)\n        self.assertEquals(Cell().python_formula, None)\n        self.assertEquals(Cell().dependencies, [])\n        self.assertEquals(Cell().value, undefined)\n        self.assertEquals(Cell().formatted_value, u'')\n        self.assertEquals(Cell().error, None)\n\n\n    def test_clear_value_clears_value_but_not_formatted_value(self):\n        cell = Cell()\n\n        cell.value = 29\n        cell.formatted_value = \"wibble\"\n\n        cell.clear_value()\n\n        self.assertEquals(cell.value, undefined)\n        self.assertEquals(cell.formatted_value, \"wibble\")\n\n\n    def test_clear_clears_stuff(self):\n        cell = Cell()\n\n        cell.value = 29\n        cell.formula = 'e equals emcee squared'\n        cell.python_formula = 'e equals emcee squared'\n        cell.dependencies = [(1, 1), (2, 2)]\n        cell.formatted_value = \"wibble\"\n        cell.error = 'a spear'\n\n        cell.clear()\n\n        self.assertEquals(cell.value, undefined)\n        self.assertEquals(cell.formula, None)\n        self.assertEquals(cell.python_formula, None)\n        self.assertEquals(Cell().dependencies, [])\n        self.assertEquals(cell.formatted_value, u\"\")\n        self.assertEquals(cell.error, None)\n\n\n    def test_setting_value_to_ws_doesnt_die(self):\n        # This is actually mostly testing that it doesn't explode\n        cell = Cell()\n        ws = Worksheet()\n        cell.value = ws\n        self.assertEquals(cell.formatted_value, unicode(ws))\n\n\n    def test_setting_value_to_undefined_sets_formatted_value_to_empty_string(self):\n        cell = Cell()\n        cell._set_formatted_value = Mock()\n        cell.value = undefined\n        self.assertCalledOnce(cell._set_formatted_value, u'')\n\n    def test_setting_value_sets_formatted_value_to_unicode_version(self):\n        cell = Cell()\n        cell._set_formatted_value = Mock()\n        cell.value = sentinel.value\n        self.assertCalledOnce(cell._set_formatted_value, unicode(sentinel.value))\n\n\n    def test_setting_formatted_value_to_string_passes_through(self):\n        cell = Cell()\n        cell.formatted_value = 'this > string'\n        self.assertEquals(cell.formatted_value, 'this > string')\n\n\n    def test_setting_formatted_value_to_non_string_explodes(self):\n        class TestObject(object):\n            pass\n        cell = Cell()\n\n        with self.assertRaises(TypeError) as mngr:\n            cell.formatted_value = TestObject()\n        self.assertEquals(str(mngr.exception), 'cell formatted_value must be str or unicode')\n\n\n    def test_setting_formatted_value_to_none(self):\n        cell = Cell()\n        cell.formatted_value = None\n        self.assertEquals(cell.formatted_value, '')\n\n\n    def test_setting_formatted_value_to_unicode(self):\n        cell = Cell()\n        cell.formatted_value = u'Sacr\\xe9 bleu!'\n        self.assertEquals(cell.formatted_value, u'Sacr\\xe9 bleu!')\n\n\n    def test_setting_formula_to_string_passes_through(self):\n        cell = Cell()\n        cell.formula = 'this < string'\n        self.assertEquals(cell.formula, 'this < string')\n\n\n    def test_setting_formula_to_non_string_explodes(self):\n        class TestObject(object):\n            pass\n        cell = Cell()\n\n        with self.assertRaises(TypeError) as mngr:\n            cell.formula = TestObject()\n        self.assertEquals(str(mngr.exception), 'cell formula must be str or unicode')\n\n\n    def test_setting_formula_to_none_works_and_sets_python_formula_to_none(self):\n        cell = Cell()\n        cell.python_formula = 'something'\n\n        cell.formula = None\n\n        self.assertEquals(cell.formula, None)\n        self.assertEquals(cell.python_formula, None)\n\n\n    def test_setting_formula_to_constant_clears_python_formula(self):\n        cell = Cell()\n        cell.python_formula = 'something'\n\n        cell.formula = 'k'\n\n        self.assertEquals(cell.python_formula, None)\n\n\n    @patch('sheet.cell.get_dependencies_from_parse_tree')\n    @patch('sheet.cell.get_python_formula_from_parse_tree')\n    @patch('sheet.cell.parser')\n    def test_setting_formula_parses_formula_sets_dependencies_then_sets_python_formula(\n            self, mock_parser, mock_get_python_formula_from_parse_tree, mock_get_dependencies_from_parse_tree\n    ):\n        call_order = []\n        def get_add_call(name, return_value):\n            def add_call(*_):\n                call_order.append(name)\n                return return_value\n            return add_call\n        mock_get_python_formula_from_parse_tree.side_effect = get_add_call('formula', sentinel.formula)\n        mock_get_dependencies_from_parse_tree.side_effect = get_add_call('dependencies', sentinel.dependencies)\n\n        cell = Cell()\n        cell.python_formula = '=something'\n\n        cell.formula = '=k'\n\n        self.assertCalledOnce(mock_parser.parse, '=k')\n\n        self.assertCalledOnce(mock_get_python_formula_from_parse_tree,\n            mock_parser.parse.return_value\n        )\n        self.assertEquals(\n            cell._python_formula,\n            sentinel.formula\n        )\n\n        self.assertCalledOnce(mock_get_dependencies_from_parse_tree,\n            mock_parser.parse.return_value\n        )\n        self.assertEquals(\n            cell.dependencies,\n            sentinel.dependencies\n        )\n\n        self.assertEquals(call_order, ['dependencies', 'formula'])\n\n\n    def test_setting_formula_with_syntax_error_sets_appropriate_raise_in_python_formula_and_clears_dependencies(self):\n        cell = Cell()\n\n        cell.dependencies = 'before'\n        cell.python_formula = 'before'\n        cell.formula = '=#NULL!'\n        self.assertEquals(\n            cell.python_formula,\n            '_raise(FormulaError(\"Error in formula at position 2: unexpected \\'#NULL!\\'\"))'\n        )\n        self.assertEquals(cell.dependencies, [])\n\n        cell.dependencies = 'before'\n        cell.python_formula = 'before'\n        cell.formula = '=#Invalid!'\n        self.assertEquals(\n            cell.python_formula,\n            '_raise(FormulaError(\"#Invalid! cell reference in formula\"))'\n        )\n        self.assertEquals(cell.dependencies, [])\n\n        cell.dependencies = 'before'\n        cell.python_formula = 'before'\n        cell.formula = '=#Deleted!'\n        self.assertEquals(\n            cell.python_formula,\n            '_raise(FormulaError(\"#Deleted! cell reference in formula\"))'\n        )\n        self.assertEquals(cell.dependencies, [])\n\n\n    def test_setting_python_formula_to_non_string_explodes(self):\n        cell = Cell()\n\n        with self.assertRaises(TypeError) as mngr:\n            cell.python_formula = type\n\n        self.assertEquals(\n                str(mngr.exception),\n                'cell python_formula must be str or unicode'\n        )\n\n\n    def test_can_use_unicode_in_formula(self):\n        cell = Cell()\n        cell.formula = u'Sacr\\xe9 bleu!'\n        self.assertEquals(cell.formula, u'Sacr\\xe9 bleu!')\n\n\n    def test_repr(self):\n        cell = Cell()\n        cell.formula = 'f'\n        self.assertEquals(repr(cell), \"<Cell formula=f value=<undefined> formatted_value=u''>\")\n\n        cell = Cell()\n        cell.value = 'v'\n        self.assertEquals(repr(cell), \"<Cell formula=None value='v' formatted_value=u'v'>\")\n\n        cell = Cell()\n        cell.value = 23\n        self.assertEquals(repr(cell), \"<Cell formula=None value=23 formatted_value=u'23'>\")\n\n        cell = Cell()\n        cell.formula = 'f'\n        cell.value = 'v'\n        self.assertEquals(repr(cell), \"<Cell formula=f value='v' formatted_value=u'v'>\")\n\n        cell = Cell()\n        cell.formula = 'f'\n        cell.value = 'v'\n        cell.formatted_value = u'fv'\n        self.assertEquals(repr(cell), \"<Cell formula=f value='v' formatted_value=u'fv'>\")\n\n        cell = Cell()\n        cell.formula = 'f'\n        cell.value = 'v'\n        cell.formatted_value = u'fv'\n        cell.error = 'e'\n        self.assertEquals(repr(cell), \"<Cell formula=f value='v' formatted_value=u'fv' error='e'>\")\n\n\n    def test_eq_neq(self):\n        cell1 = Cell()\n        cell1.formula = '=formula'\n        cell2 = Cell()\n        cell2.formula = '=formula'\n        self.assertTrue(cell1 == cell2)\n        self.assertFalse(cell1 != cell2)\n\n        cell1.value = 10\n        self.assertFalse(cell1 == cell2)\n        self.assertTrue(cell1 != cell2)\n        cell2.value = 10\n        self.assertTrue(cell1 == cell2)\n        self.assertFalse(cell1 != cell2)\n\n        cell1.formatted_value = 'formatted'\n        self.assertFalse(cell1 == cell2)\n        self.assertTrue(cell1 != cell2)\n        cell2.formatted_value = 'formatted'\n        self.assertTrue(cell1 == cell2)\n        self.assertFalse(cell1 != cell2)\n\n        cell1.error = 'this error'\n        self.assertFalse(cell1 == cell2)\n        self.assertTrue(cell1 != cell2)\n        cell2.error = 'this error'\n        self.assertTrue(cell1 == cell2)\n        self.assertFalse(cell1 != cell2)\n\n        self.assertFalse(cell1 == 'hello')\n        self.assertTrue(cell1 != 'hello')\n"
  },
  {
    "path": "dirigible/sheet/tests/test_cell_range.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\nfrom __future__ import with_statement\n\ntry:\n    import unittest2 as unittest\nexcept ImportError:\n    import unittest\n\nfrom mock import Mock\n\nfrom sheet.cell import Cell, undefined\nfrom sheet.worksheet import Worksheet\nfrom sheet.cell_range import CellRange\nfrom dirigible.test_utils import ResolverTestCase\n\n\n\nclass TestCellRanges(ResolverTestCase):\n    def setUp(self):\n        self.ws = ws = Worksheet()\n        ws[1, 1].value = 'foist'\n        ws[10, 100].value = 'second'\n        ws[2, 3].value = 'middle1'\n        ws[3, 3].value = 'middle2'\n        ws[2, 4].value = 'raster test'\n        ws[100, 100].value = 'way down here'\n\n    def test_constructor_with_only_worksheet_fails(self):\n        self.assertRaises(TypeError, lambda : CellRange(self.ws))\n\n    def test_constructor_with_start_and_end(self):\n        cell_range = CellRange(self.ws, (2, 3), (4, 5))\n        self.assertEquals(cell_range.left, 2)\n        self.assertEquals(cell_range.top, 3)\n        self.assertEquals(cell_range.right, 4)\n        self.assertEquals(cell_range.bottom, 5)\n        self.assertEquals(cell_range.worksheet, self.ws)\n\n    def test_constructor_inverted(self):\n        cell_range = CellRange(self.ws, (4, 5), (2, 3))\n        self.assertEquals(cell_range.left, 2)\n        self.assertEquals(cell_range.top, 3)\n        self.assertEquals(cell_range.right, 4)\n        self.assertEquals(cell_range.bottom, 5)\n\n        cell_range = CellRange(self.ws, (4, 3), (2, 5))\n        self.assertEquals(cell_range.left, 2)\n        self.assertEquals(cell_range.top, 3)\n        self.assertEquals(cell_range.right, 4)\n        self.assertEquals(cell_range.bottom, 5)\n\n    def test_equality(self):\n        cr1 = CellRange(self.ws, (2, 3), (4, 5))\n        cr2 = CellRange(self.ws, (2, 3), (4, 5))\n\n        self.assertTrue(cr1 == cr2)\n        self.assertFalse(cr1 != cr2)\n\n        cr_diff_sheet = CellRange(Worksheet(), (2, 3), (4, 5))\n        self.assertFalse(cr1 == cr_diff_sheet)\n        self.assertTrue(cr1 != cr_diff_sheet)\n\n        cr_diff_start = CellRange(self.ws, (2, 33), (4, 5))\n        self.assertFalse(cr1 == cr_diff_start)\n        self.assertTrue(cr1 != cr_diff_start)\n\n        cr_diff_end = CellRange(self.ws, (2, 3), (4, 55))\n        self.assertFalse(cr1 == cr_diff_end)\n        self.assertTrue(cr1 != cr_diff_end)\n\n        self.assertFalse(cr1 == 15)\n        self.assertTrue(cr1 != 15)\n\n    def test_repr(self):\n        self.ws.name = 'AWESOME-SHEET'\n        cr1 = CellRange(self.ws, (2, 3), (4, 5))\n        self.assertEquals(str(cr1), '<CellRange B3 to D5 in <Worksheet AWESOME-SHEET>>')\n\n    def test_edges(self):\n        cell_range = CellRange(self.ws, (2, 4), (3, 5))\n        self.assertEquals(cell_range.left, 2)\n        self.assertEquals(cell_range.right, 3)\n        self.assertEquals(cell_range.top, 4)\n        self.assertEquals(cell_range.bottom, 5)\n\n    def test_len(self):\n        cell_range = CellRange(self.ws, (2, 3), (3, 5))\n        self.assertEquals(len(cell_range), 6)\n\n\n    def test_iterators_with_start_and_end(self):\n        cell_range = CellRange(self.ws, (2, 3), (3, 5))\n        expected_locs = [(2, 3), (3, 3), (2, 4), (3, 4), (2, 5), (3, 5)]\n        self.assertEquals(len(list(cell_range.cells)), 6)\n        self.assertEquals(list(cell_range.cells), [self.ws[loc] for loc in expected_locs])\n        self.assertEquals(list(cell_range), [self.ws[loc].value for loc in expected_locs])\n\n\n    def test_iterators_with_start_and_end_inverted(self):\n        cell_range = CellRange(self.ws, (3, 5), (2, 3))\n        expected_cells = [self.ws[loc] for loc in [(2, 3), (3, 3), (2, 4), (3, 4),(2, 5),(3, 5),]]\n        self.assertEquals(list(cell_range), [c.value for c in expected_cells])\n        self.assertEquals(list(cell_range.cells), expected_cells)\n\n\n    def test_indexing_by_tuple(self):\n        cell_range = CellRange(self.ws, (2, 3), (10, 10))\n\n        existing_cell = self.ws[3, 3]\n        self.assertEquals(cell_range[2, 1], existing_cell)\n\n        blank_cell_from_range = cell_range[4, 4]\n        self.assertEquals(blank_cell_from_range.value, undefined)\n\n        blank_cell_from_range.value = \"new cell's value\"\n        self.assertEquals(self.ws[5, 6].value, \"new cell's value\")\n\n        overly_large_offset = (100, 100)\n        self.assertRaises(IndexError, lambda : cell_range[overly_large_offset])\n\n    def test_indexing_edge_cases(self):\n        cell_range = CellRange(self.ws, (2, 2), (10, 9))\n\n        self.assertEquals(cell_range[1, 1], self.ws[2, 2])\n        self.assertEquals(cell_range[9, 8], self.ws[10, 9])\n\n        try:\n            cell_range[10, 8]\n            self.fail('should have raised error')\n        except IndexError, e:\n            self.assertEquals(str(e), 'Cell range only has 9 columns')\n\n        try:\n            cell_range[9, 9]\n            self.fail('should have raised error')\n        except IndexError, e:\n            self.assertEquals(str(e), 'Cell range only has 8 rows')\n\n        try:\n            cell_range[0, 0]\n            self.fail('should have raised error')\n        except IndexError, e:\n            self.assertEquals(str(e), 'Cell ranges are 1-indexed')\n\n    def test_indexing_backwards(self):\n        cell_range = CellRange(self.ws, (2, 2), (10, 9))\n\n        self.assertEquals(cell_range[-1, -1], self.ws[10, 9])\n        self.assertEquals(cell_range[-4, 2], self.ws[7, 3])\n\n        try:\n            cell_range[-10, -9]\n            self.fail('should have raised error')\n        except IndexError, e:\n            self.assertEquals(str(e), 'Cell range only has 9 columns')\n\n    def test_setitem_on_nonexistent_cell(self):\n        cell_range = CellRange(self.ws, (2, 2), (10, 9))\n        cell_range[2, 2].value = 'new val'\n        self.assertEquals(self.ws[3, 3].value, 'new val')\n\n        new_cell = Cell()\n        cell_range[2, 3] = new_cell\n        self.assertEquals(self.ws[3, 4], new_cell)\n\n\n    def test_iterator_with_locations(self):\n        cell_range = CellRange(self.ws, (1, 2), (3, 4))\n        self.assertEquals(len(list(cell_range.cells)),\n                          len(list(cell_range.locations_and_cells)))\n        for (loc, cell), actual_cell in zip(\n                cell_range.locations_and_cells,\n                cell_range.cells):\n            self.assertTrue(cell is self.ws[loc])\n            self.assertTrue(cell is actual_cell)\n\n\n    def test_clear_should_call_clear_on_member_cells(self):\n        cell_range = CellRange(self.ws, (1, 2), (2, 3))\n        for cell in cell_range.cells:\n            cell.clear = Mock()\n        cell_range.clear()\n        for cell in cell_range.cells:\n            self.assertCalledOnce(cell.clear)\n\n\n\n"
  },
  {
    "path": "dirigible/sheet/tests/test_clipboard.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\ntry:\n    import simplejson as json\nexcept ImportError:\n    import json\n\nfrom mock import Mock, patch\n\nfrom django.contrib.auth.models import User\n\nfrom dirigible.test_utils import ResolverTestCase\n\nfrom sheet.models import Clipboard, Sheet\nfrom sheet.worksheet import Cell, Worksheet\n\n\nclass ClipboardModelTest(ResolverTestCase):\n\n    def test_initial_fields(self):\n        user = User()\n        user.username = 'testuser'\n        clipboard = Clipboard(owner=user)\n        self.assertEquals(clipboard.owner,  user)\n        self.assertEquals(clipboard.contents_json, '{}')\n        self.assertEquals(clipboard.is_cut, False)\n        self.assertEquals(clipboard.source_left, None)\n        self.assertEquals(clipboard.source_top, None)\n        self.assertEquals(clipboard.source_right, None)\n        self.assertEquals(clipboard.source_bottom, None)\n        self.assertEquals(clipboard.source_sheet, None)\n\n\n    def test_source_range(self):\n        user = User()\n        user.username = 'testuser'\n        clipboard = Clipboard(owner=user)\n        clipboard.source_left = 1\n        clipboard.source_top = 2\n        clipboard.source_right = 3\n        clipboard.source_bottom = 4\n        self.assertEquals(clipboard.source_range, (1, 2, 3, 4))\n\n    def test_width_and_height(self):\n        user = User()\n        user.username = 'testuser'\n        clipboard = Clipboard(owner=user)\n        clipboard.source_left = 1\n        clipboard.source_top = 2\n        clipboard.source_right = 3\n        clipboard.source_bottom = 5\n        self.assertEquals(clipboard.width, 3)\n        self.assertEquals(clipboard.height, 4)\n\n\n    def test_clipboad_copy_retrieves_stuff_from_sheet_and_removes_offset(self):\n        self.maxDiff = None\n        clipboard = Clipboard()\n        sheet = Sheet()\n        worksheet = Worksheet()\n        worksheet.C2.formula = 'foo'\n        worksheet.C2.formatted_value = 'fv'\n        worksheet.D2.formatted_value = 'fv to become formula'\n\n        sheet.jsonify_worksheet(worksheet)\n        start = (3, 2)\n        end = (4, 3)\n        cut_called_previously = True\n        clipboard.is_cut = cut_called_previously\n        clipboard.copy(sheet, start, end)\n        self.assertEquals(\n            json.loads(clipboard.contents_json),\n            {\n                '0,0':{'formula': 'foo',\n                    'formatted_value': 'fv'},\n                '1,0':{'formula': 'fv to become formula',\n                    'formatted_value': 'fv to become formula'},\n                '0,1':{'formula': '',\n                    'formatted_value': ''},\n                '1,1':{'formula': '',\n                    'formatted_value': ''}\n            }\n        )\n        self.assertEquals(clipboard.source_left, 3)\n        self.assertEquals(clipboard.source_top, 2)\n        self.assertEquals(clipboard.source_right, 4)\n        self.assertEquals(clipboard.source_bottom, 3)\n\n        self.assertEquals(clipboard.is_cut, False)\n        self.assertEquals(clipboard.source_sheet, None)\n\n\n    def test_cut_calls_copy_then_cuts_and_remembers_some_stuff(self):\n        clipboard = Clipboard()\n        sheet = Sheet()\n        user = User()\n        user.username = 'test_cuttter'\n        user.save()\n        sheet.owner = user\n        clipboard.copy = Mock(wraps=clipboard.copy)\n\n        worksheet = Worksheet()\n        worksheet.A1.formula = 'outside'\n        worksheet.A2.formula = 'inside'\n        sheet.jsonify_worksheet(worksheet)\n        sheet.save()\n\n        clipboard.cut(sheet, (1, 2), (3, 4))\n\n        self.assertCalledOnce(clipboard.copy, sheet, (1, 2), (3, 4))\n\n        worksheet = sheet.unjsonify_worksheet()\n\n        self.assertEquals(worksheet.A1.formula, 'outside')\n        self.assertEquals(worksheet.A2, Cell())\n\n        self.assertEquals(clipboard.source_left, 1)\n        self.assertEquals(clipboard.source_top, 2)\n        self.assertEquals(clipboard.source_right, 3)\n        self.assertEquals(clipboard.source_bottom, 4)\n\n        self.assertEquals(clipboard.is_cut, True)\n        self.assertEquals(clipboard.source_sheet, sheet)\n\n\n    @patch('sheet.clipboard.StringIO')\n    def test_clipboard_remembers_to_close_stingIO_stream(self, mock_stringio):\n        sheet = Sheet()\n        clipboard = Clipboard()\n        mock_stringio.StringIO.return_value = Mock()\n        clipboard.copy(sheet, (1, 1), (1, 1))\n        self.assertCalledOnce(mock_stringio.StringIO.return_value.close)\n\n\n    def test_clipboard_json_to_cells(self):\n        self.maxDiff = None\n        clipboard = Clipboard()\n        clipboard.source_left = 1\n        clipboard.source_top = 2\n        clipboard.source_right = 2\n        clipboard.source_bottom = 3\n        strings_dict = {\n                '0,0':{'formula': 'foo',\n                    'formatted_value': 'fv'},\n                '1,0':{'formula': 'fv to become formula',\n                    'formatted_value': 'fv to become formula'},\n                '0,1':{'formula': '',\n                    'formatted_value': ''},\n                '1,1':{'formula': '',\n                    'formatted_value': ''}\n            }\n        clipboard.contents_json = json.dumps(strings_dict)\n\n        c2 = Cell()\n        c2.formula = 'foo'\n        c2.formatted_value = 'fv'\n        d2 = Cell()\n        d2.formula = 'fv to become formula'\n        d2.formatted_value = d2.formula\n        c3 = Cell()\n        d3 = Cell()\n        expected_cells = {\n            (0, 0): c2,\n            (0, 1): c3,\n            (1, 0): d2,\n            (1, 1): d3,\n        }\n\n        self.assertEquals(dict(clipboard.to_cells((0, 0), (1, 1))), expected_cells)\n\n\n    def test_paste_to_for_copy(self):\n        c2 = Cell()\n        c2.formula = 'foo'\n        c2.formatted_value = 'fv'\n        d2 = Cell()\n        d2.formula = 'fv to become formula'\n        d2.formatted_value = d2.formula\n        c3 = Cell()\n        d3 = Cell()\n        cells = {\n            (2, 2): c2,\n            (2, 3): c3,\n            (3, 2): d2,\n            (3, 3): d3,\n        }\n\n        clipboard = Clipboard()\n        clipboard.source_left = 1\n        clipboard.source_top = 1\n        clipboard.source_right = 2\n        clipboard.source_bottom = 2\n        clipboard.to_cells = Mock()\n        clipboard.to_cells.return_value = cells.iteritems()\n\n        sheet = Sheet()\n        worksheet = Worksheet()\n        worksheet.B3.formula = 'i am in danger!'\n        worksheet.A1.formula = 'i am safe :-)'\n        sheet.jsonify_worksheet(worksheet)\n\n        clipboard.paste_to(sheet, (2, 2), (2, 2))\n\n        updated_worksheet = sheet.unjsonify_worksheet()\n        self.assertEquals(updated_worksheet.A1.formula, 'i am safe :-)')\n        self.assertEquals(updated_worksheet.B2.formula, c2.formula)\n        self.assertEquals(updated_worksheet.C2.formula, d2.formula)\n        self.assertEquals(updated_worksheet.B3.formula, c3.formula)\n        self.assertEquals(updated_worksheet.C3.formula, d3.formula)\n\n\n    def test_paste_to_for_cut_different_sheet(self):\n        self.user = User()\n        self.user.save()\n\n        c2 = Cell()\n        c2.formula = 'foo'\n        c2.formatted_value = 'fv'\n        d2 = Cell()\n        d2.formula = 'fv to become formula'\n        d2.formatted_value = d2.formula\n        c3 = Cell()\n        d3 = Cell()\n        c5 = Cell()\n        c5.formula = 'a safe source cell'\n        dest_cells = {\n            (3, 4): c2,\n            (3, 5): c3,\n            (4, 4): d2,\n            (4, 5): d3,\n        }\n\n        source_sheet = Sheet()\n        source_sheet.owner = self.user\n        source_sheet.save()\n\n        clipboard = Clipboard()\n        clipboard.to_cells = Mock()\n        clipboard.to_cells.return_value = dest_cells.iteritems()\n        clipboard.is_cut = True\n        clipboard.source_sheet = source_sheet\n        clipboard.source_left = 3\n        clipboard.source_top = 2\n        clipboard.source_right = 4\n        clipboard.source_bottom = 3\n\n        destination_sheet = Sheet()\n        destination_sheet.owner = self.user\n        destination_worksheet = Worksheet()\n        destination_worksheet.C4.formula = 'i am in danger!'\n        destination_worksheet.A1.formula = 'i am safe :-)'\n        destination_sheet.jsonify_worksheet(destination_worksheet)\n        destination_sheet.save()\n\n        clipboard.paste_to(destination_sheet, (3, 4), (3, 4))\n\n        updated_worksheet = destination_sheet.unjsonify_worksheet()\n        self.assertEquals(updated_worksheet.A1.formula, 'i am safe :-)')\n        self.assertEquals(updated_worksheet.C4.formula, c2.formula)\n        self.assertEquals(updated_worksheet.D4.formula, d2.formula)\n        self.assertEquals(updated_worksheet.C5.formula, c3.formula)\n        self.assertEquals(updated_worksheet.d5.formula, d3.formula)\n\n        # paste should reset the clipboard so that future pastes act as\n        # though they came from a copy\n        self.assertEquals(clipboard.source_left, 3)\n        self.assertEquals(clipboard.source_top, 4)\n        self.assertEquals(clipboard.source_right, 4)\n        self.assertEquals(clipboard.source_bottom, 5)\n        self.assertEquals(clipboard.is_cut, False)\n        self.assertEquals(clipboard.source_sheet, None)\n\n\n    def test_copy_then_paste_same_sheet(self):\n        sheet = Sheet()\n        worksheet = Worksheet()\n        worksheet.A1.formula = 'safe'\n        worksheet.A2.formula = 'topleft'\n        worksheet.B2.formula = 'topright'\n        worksheet.A3.formula = 'bottomleft'\n        worksheet.B3.formula = 'bottomright'\n        sheet.jsonify_worksheet(worksheet)\n\n        clipboard = Clipboard()\n        clipboard.copy(sheet, (1, 2), (2, 3))\n        clipboard.paste_to(sheet, (2, 3), (2, 3))\n\n        updated_worksheet = sheet.unjsonify_worksheet()\n        self.assertEquals(updated_worksheet.A1.formula, 'safe')\n        self.assertEquals(updated_worksheet.A2.formula, 'topleft')\n        self.assertEquals(updated_worksheet.B2.formula, 'topright')\n        self.assertEquals(updated_worksheet.A3.formula, 'bottomleft')\n        self.assertEquals(updated_worksheet.C3.formula, 'topright')\n        self.assertEquals(updated_worksheet.C4.formula, 'bottomright')\n        self.assertEquals(updated_worksheet.B4.formula, 'bottomleft')\n        self.assertEquals(updated_worksheet.B3.formula, 'topleft')\n\n\n    def test_cut_then_paste_same_sheet(self):\n        sheet = Sheet()\n        worksheet = Worksheet()\n        worksheet.A1.formula = 'safe'\n        worksheet.A2.formula = 'topleft'\n        worksheet.B2.formula = 'topright'\n        worksheet.A3.formula = 'bottomleft'\n        worksheet.B3.formula = 'bottomright'\n        sheet.jsonify_worksheet(worksheet)\n\n        clipboard = Clipboard()\n        clipboard.cut(sheet, (1, 2), (2, 3))\n\n        updated_worksheet = sheet.unjsonify_worksheet()\n        self.assertEquals(updated_worksheet.A1.formula, 'safe')\n        self.assertEquals(updated_worksheet.A2, Cell() )\n        self.assertEquals(updated_worksheet.B2, Cell() )\n        self.assertEquals(updated_worksheet.A3, Cell() )\n\n        clipboard.paste_to(sheet, (2, 3), (2, 3))\n\n        updated_worksheet = sheet.unjsonify_worksheet()\n\n        self.assertEquals(updated_worksheet.A1.formula, 'safe')\n        self.assertEquals(updated_worksheet.A2, Cell() )\n        self.assertEquals(updated_worksheet.B2, Cell() )\n        self.assertEquals(updated_worksheet.A3, Cell() )\n        self.assertEquals(updated_worksheet.B3.formula, 'topleft')\n        self.assertEquals(updated_worksheet.C3.formula, 'topright')\n        self.assertEquals(updated_worksheet.C4.formula, 'bottomright')\n        self.assertEquals(updated_worksheet.B4.formula, 'bottomleft')\n\n\n    def test_paste_to_should_tile_clipboard_contents_across_selected_range(self):\n        sheet = Sheet()\n        worksheet = Worksheet()\n        worksheet.A1.formula = '=c1'\n        worksheet.B1.formula = '=d1'\n        worksheet.A2.formula = '=c2'\n        worksheet.B2.formula = '=d2'\n        sheet.jsonify_worksheet(worksheet)\n\n        clipboard = Clipboard()\n        clipboard.copy(sheet, (1, 1), (2, 2))\n        clipboard.paste_to(sheet, (3, 3), (5, 7))\n\n        worksheet = sheet.unjsonify_worksheet()\n        self.assertEquals(worksheet.C3.formula, '=E3')\n        self.assertEquals(worksheet.D3.formula, '=F3')\n        self.assertEquals(worksheet.E3.formula, '=G3')\n        self.assertEquals(worksheet.F3.formula, None)\n\n        self.assertEquals(worksheet.C4.formula, '=E4')\n        self.assertEquals(worksheet.D4.formula, '=F4')\n        self.assertEquals(worksheet.E4.formula, '=G4')\n        self.assertEquals(worksheet.F4.formula, None)\n\n        self.assertEquals(worksheet.C5.formula, '=E5')\n        self.assertEquals(worksheet.D5.formula, '=F5')\n        self.assertEquals(worksheet.E5.formula, '=G5')\n        self.assertEquals(worksheet.F5.formula, None)\n\n        self.assertEquals(worksheet.C6.formula, '=E6')\n        self.assertEquals(worksheet.D6.formula, '=F6')\n        self.assertEquals(worksheet.E6.formula, '=G6')\n        self.assertEquals(worksheet.F6.formula, None)\n\n        self.assertEquals(worksheet.C7.formula, '=E7')\n        self.assertEquals(worksheet.D7.formula, '=F7')\n        self.assertEquals(worksheet.E7.formula, '=G7')\n        self.assertEquals(worksheet.F7.formula, None)\n\n        self.assertEquals(worksheet.C8.formula, None)\n        self.assertEquals(worksheet.D8.formula, None)\n        self.assertEquals(worksheet.E8.formula, None)\n        self.assertEquals(worksheet.F8.formula, None)\n\n\nclass FormulaRewriteTest(ResolverTestCase):\n\n    @patch('sheet.clipboard.rewrite_formula')\n    def test_formulae_are_rewritten(self, mock_rewrite):\n        mock_rewrite.return_value = '=C8'\n\n        sheet = Sheet()\n        worksheet = Worksheet()\n        worksheet.A1.formula = '=B6'\n        sheet.jsonify_worksheet(worksheet)\n        clipboard = Clipboard()\n        clipboard.copy(sheet, (1, 1), (1, 1))\n\n        clipboard.paste_to(sheet, (2, 3), (2, 3))\n\n        self.assertCalledOnce(\n            mock_rewrite,\n            '=B6', 1, 2, False, (1, 1, 1, 1)\n        )\n\n\n    @patch('sheet.clipboard.rewrite_source_sheet_formulae_for_cut')\n    def test_paste_from_cut_rewrites_source_worksheet_formulae_before_pasting(\n            self, mock_rewrite_source_sheet_formulae):\n        source_worksheet = Worksheet()\n        source_worksheet.A1.formula = '=pre-rewrite'\n        source_sheet = Sheet()\n        source_sheet.jsonify_worksheet(source_worksheet)\n\n        clipboard = Clipboard()\n        clipboard.cut(source_sheet, (1, 2), (3, 5))\n        clipboard.to_cells = Mock()\n        clipboard.to_cells.return_value = {}.items()\n\n        def check_to_cells_not_run(*args):\n            self.assertFalse(clipboard.to_cells.call_args_list)\n\n        mock_rewrite_source_sheet_formulae.side_effect = check_to_cells_not_run\n\n        clipboard.paste_to(source_sheet, (2, 3), (2, 3))\n\n        self.assertCalledOnce(\n            mock_rewrite_source_sheet_formulae,\n            source_worksheet, (1, 2, 3, 5), 2, 3\n        )\n\n\n    @patch('sheet.clipboard.rewrite_source_sheet_formulae_for_cut')\n    def test_paste_from_copy_does_not_rewrite_source_sheet_formulae(\n            self, mock_rewrite_source_sheet_formulae):\n        source_worksheet = Worksheet()\n        source_worksheet.A1.formula = '=pre-rewrite'\n        source_sheet = Sheet()\n        source_sheet.jsonify_worksheet(source_worksheet)\n\n        clipboard = Clipboard()\n        clipboard.copy(source_sheet, (1, 2), (3, 5))\n        clipboard.to_cells = Mock()\n        clipboard.to_cells.return_value = {}.items()\n\n        def check_to_cells_not_run(*args):\n            self.assertFalse(clipboard.to_cells.call_args_list)\n\n        mock_rewrite_source_sheet_formulae.side_effect = check_to_cells_not_run\n\n        clipboard.paste_to(source_sheet, (2, 3), (2, 3))\n\n        self.assertFalse( mock_rewrite_source_sheet_formulae.called )\n\n\n    @patch('sheet.clipboard.rewrite_source_sheet_formulae_for_cut')\n    def test_paste_onto_different_sheet_from_cut_does_not_rewrite_source_sheet_formulae(\n            self, mock_rewrite_source_sheet_formulae\n        ):\n        source_worksheet = Worksheet()\n        source_worksheet.A1.formula = '=pre-rewrite'\n        source_sheet = Sheet()\n        source_sheet.jsonify_worksheet(source_worksheet)\n\n        dest_sheet = Mock()\n\n        clipboard = Clipboard()\n        clipboard.cut(source_sheet, (1, 2), (3, 5))\n        clipboard.to_cells = Mock()\n        clipboard.to_cells.return_value = {}.items()\n\n        def check_to_cells_not_run(*args):\n            self.assertFalse(clipboard.to_cells.call_args_list)\n\n        mock_rewrite_source_sheet_formulae.side_effect = check_to_cells_not_run\n\n        clipboard.paste_to(dest_sheet, (2, 3), (2, 3))\n\n        self.assertFalse( mock_rewrite_source_sheet_formulae.called )\n\n\n"
  },
  {
    "path": "dirigible/sheet/tests/test_dependency_graph.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\n\nfrom mock import call, Mock, patch, sentinel\n\nfrom dirigible.test_utils import die, ResolverTestCase\n\nfrom sheet.cell import Cell\nfrom sheet.dependency_graph import (\n    _add_location_dependencies, build_dependency_graph,\n    _generate_cell_subgraph, Node)\nfrom sheet.errors import (\n    CycleError, report_cell_error,\n)\nfrom sheet.worksheet import Worksheet\n\n\nclass TestBuildDependencyGraph(ResolverTestCase):\n\n    def test_returns_graph_and_leaf_nodes(self):\n        worksheet = Worksheet()\n        worksheet[1, 1].formula = '=A2 + B2'\n        worksheet[1, 2].formula = '=A3'\n        worksheet[2, 2].formula = '=B3'\n        worksheet[1, 3].formula = '=1'\n        worksheet[2, 3].formula = '1'\n        worksheet[3, 3].python_formula = '1'\n\n        graph, leaves = build_dependency_graph(worksheet)\n\n        self.maxDiff = None\n        self.assertEquals(\n            graph,\n            {\n                (1, 1): Node(\n                    (1, 1),\n                    children=set([(1, 2), (2, 2)]),\n                    parents=set()\n                ),\n                (1, 2): Node(\n                    (1, 2),\n                    children=set([(1, 3)]),\n                    parents=set([(1, 1)])\n                ),\n                (2, 2): Node(\n                    (2, 2),\n                    children=set(),\n                    parents=set([(1, 1)])\n                ),\n                (1, 3): Node(\n                    (1, 3),\n                    children=set(),\n                    parents=set([(1, 2)])\n                ),\n                (3, 3): Node(\n                    (3, 3),\n                    children=set(),\n                    parents=set()\n                ),\n            }\n        )\n        self.assertEquals(set(leaves), set([(1, 3), (2, 2), (3, 3)]))\n\n        worksheet[1, 2].formula = '=A3 + B3'\n        graph, leaves = build_dependency_graph(worksheet)\n\n        self.assertEquals(graph, {\n            (1, 1): Node(\n                (1, 1),\n                children=set([(1, 2), (2, 2)]),\n                parents=set(),\n            ),\n            (1, 2): Node(\n                (1, 2),\n                children=set([(1, 3)]),\n                parents=set([(1, 1)]),\n            ),\n            (2, 2): Node(\n                (2, 2),\n                children=set(),\n                parents=set([(1, 1)])\n            ),\n            (1, 3): Node(\n                (1, 3),\n                children=set(),\n                parents=set([(1, 2)])\n            ),\n            (3, 3): Node(\n                (3, 3),\n                children=set(),\n                parents=set()\n            ),\n        })\n        self.assertEquals(set(leaves), set([(1, 3), (2, 2), (3, 3)]))\n\n\n    def test_is_robust_against_references_to_empty_cells(self):\n        worksheet = Worksheet()\n        worksheet[1, 1].formula = '=A2'\n\n        # NB we're making sure that this call doesn't raise an error\n        # because the cell A2 is created in the dictionary while we're\n        # iterating over it.\n        graph, leaves = build_dependency_graph(worksheet)\n\n        self.maxDiff = None\n        self.assertEquals(\n            graph,\n            {\n                (1, 1): Node(\n                    (1, 1),\n                    children=set(),\n                    parents=set()\n                )\n            }\n        )\n        self.assertEquals(leaves, [(1, 1)])\n\n\n    @patch('sheet.dependency_graph.report_cell_error')\n    def test_puts_errors_on_cells_in_cycles_and_omits_them_from_graph(self, mock_report_cell_error):\n        mock_report_cell_error.side_effect = report_cell_error\n        worksheet = Worksheet()\n        worksheet[1, 1].formula = '=A2'\n        worksheet[1, 2].formula = '=A1'\n        worksheet[1, 3].formula = '=A1'\n        worksheet[1, 4].formula = '=A5'\n        worksheet[1, 5].formula = '=5'\n\n        graph, leaves = build_dependency_graph(worksheet)\n        self.assertEquals(\n            graph,\n            {\n                (1, 3): Node((1, 3), children=set(), parents=set()),\n                (1, 4): Node((1, 4), children=set([(1, 5)]), parents=set()),\n                (1, 5): Node((1, 5), children=set(), parents=set([(1, 4)])),\n            }\n        )\n        self.assertEquals(leaves, [(1, 5), (1, 3)])\n        a1_cycle_error = CycleError([(1, 2), (1, 1), (1, 2)])\n        self.assertEquals(\n            mock_report_cell_error.call_args_list,\n            [\n                call(worksheet, (1, 2), a1_cycle_error),\n                call(worksheet, (1, 1), a1_cycle_error),\n            ]\n        )\n\n\nclass TestGenerateCellSubgraph(ResolverTestCase):\n\n    @patch('sheet.dependency_graph._generate_cell_subgraph')\n    @patch('sheet.dependency_graph._add_location_dependencies')\n    def test_should_recursively_call_itself_on_dependencies_before_adding_dependencies_to_graph(\n        self, mock_add_location_dependencies, mock_generate_cell_subgraph\n    ):\n        mock_generate_cell_subgraph.copied_call_args_list = []\n\n        def mock_recalc_recursive_call(worksheet, context, loc, visited, path):\n            self.assertFalse(mock_add_location_dependencies.called)\n            mock_generate_cell_subgraph.copied_call_args_list.append((worksheet, context, loc, set(visited), list(path)))\n\n\n        mock_generate_cell_subgraph.side_effect = mock_recalc_recursive_call\n        mock_generate_cell_subgraph_was_called_before_add_location_dependencies = []\n\n        def add_location_dependencies_side_effect(*_):\n            mock_generate_cell_subgraph_was_called_before_add_location_dependencies.append(mock_generate_cell_subgraph.called)\n        mock_add_location_dependencies.side_effect = add_location_dependencies_side_effect\n\n        worksheet = Worksheet()\n        worksheet[1, 11].formula = '=formula'\n        worksheet[1, 11].dependencies = [(2, 22), (3, 33)]\n        context = sentinel.context\n\n        _generate_cell_subgraph(worksheet, context, (1, 11), set(), [])\n\n        self.assertTrue(mock_add_location_dependencies.called)\n        self.assertTrue(mock_generate_cell_subgraph_was_called_before_add_location_dependencies[0])\n        self.assertItemsEqual(\n            mock_generate_cell_subgraph.copied_call_args_list,\n            [\n                (worksheet, context, (2, 22), set(), [(1, 11)]),\n                (worksheet, context, (3, 33), set(), [(1, 11)]),\n            ]\n        )\n\n\n    @patch('sheet.dependency_graph._add_location_dependencies')\n    def test_should_add_dependencies_to_graph(\n        self, mock_add_location_dependencies\n    ):\n        worksheet = Worksheet()\n        worksheet[99, 98].formula = '=foobar'\n        worksheet[1, 11].formula = '=foo'\n        worksheet[1, 11].dependencies = [(99, 98)]\n        graph = sentinel.graph\n\n        _generate_cell_subgraph(worksheet, graph, (1, 11), set(), [])\n\n        self.assertEqual(\n            mock_add_location_dependencies.call_args,\n            ((graph, (1, 11), set([(99, 98)])), {}),\n        )\n\n\n    @patch('sheet.dependency_graph._add_location_dependencies')\n    def test_should_remove_dependencies_with_errors_and_empty_cells(\n        self, mock_add_location_dependencies\n    ):\n        worksheet = Worksheet()\n        worksheet[1, 1].formula = '1'\n        worksheet[1, 2].error = CycleError([])\n        worksheet[1, 3].error = SyntaxError('')\n        worksheet[1, 11].formula = '=foo'\n        worksheet[1, 11].dependencies = [(1, 1), (1, 2), (1, 3), (1, 4)]\n        graph = sentinel.graph\n\n        _generate_cell_subgraph(worksheet, graph, (1, 11), set(), [])\n\n        self.assertCalledOnce(mock_add_location_dependencies,\n                              graph, (1, 11), set())\n\n\n    @patch('sheet.dependency_graph._generate_cell_subgraph', die(CycleError([])))\n    @patch('sheet.dependency_graph._add_location_dependencies')\n    @patch('sheet.dependency_graph.report_cell_error')\n    def test_should_report_cell_error_and_not_add_location_on_recursive_call_raising_cycle_error_if_location_is_not_in_cycle_path(\n        self, mock_report_cell_error, mock_add_location_dependencies\n    ):\n        worksheet = Worksheet()\n        worksheet[1, 11].formula = '=A12'\n        worksheet[1, 11].dependencies = [(1, 12)]\n\n        _generate_cell_subgraph(worksheet, sentinel.graph, (1, 11), set(), [])\n\n        self.assertCalledOnce(mock_add_location_dependencies, sentinel.graph, (1, 11), set())\n        self.assertCalledOnce(mock_report_cell_error, worksheet, (1, 11), CycleError([]))\n\n\n    @patch('sheet.dependency_graph._add_location_dependencies')\n    def test_should_add_cell_to_graph_if_formula_not_set_but_python_formula_is(\n        self, mock_add_location_dependencies\n    ):\n        worksheet = Worksheet()\n        worksheet[1, 2].python_formula = 'blerk'\n\n        _generate_cell_subgraph(worksheet, sentinel.graph, (1, 2), set(), [])\n\n        self.assertCalledOnce(mock_add_location_dependencies, sentinel.graph, (1, 2), set())\n\n\n    @patch('sheet.dependency_graph._add_location_dependencies')\n    def test_should_not_reprocess_locations_already_in_visited_even_if_it_is_in_worksheet(\n        self, mock_add_location_dependencies\n    ):\n        cell = Cell()\n        cell.formula = 'constant'\n        worksheet = Worksheet()\n        worksheet[1, 2] = cell\n\n        _generate_cell_subgraph(worksheet, sentinel.graph, (1, 2), set([(1, 2)]), [])\n\n        self.assertFalse(mock_add_location_dependencies.called)\n\n\n    @patch('sheet.dependency_graph._generate_cell_subgraph')\n    @patch('sheet.dependency_graph._add_location_dependencies', Mock())\n    def test_should_add_location_to_visited_set_after_recursing_deps(\n        self, mock_generate_cell_subgraph\n    ):\n        visited = set()\n        visited_set_at_time_of_recursive_call = []\n        # NB: Clone visited or changes will be reflected in the one we store\n        mock_generate_cell_subgraph.side_effect = lambda *_: visited_set_at_time_of_recursive_call.append(set(visited))\n\n        worksheet = Worksheet()\n        worksheet[1, 2].formula = '=23'\n        worksheet[1, 2].dependencies = [(3, 4)]\n\n        _generate_cell_subgraph(worksheet, sentinel.graph, (1, 2), visited, [])\n\n        self.assertEquals(visited_set_at_time_of_recursive_call[0], set())\n        self.assertEquals(visited, set([(1, 2)]))\n        self.assertTrue(mock_generate_cell_subgraph.called)\n\n\n    def test_should_safely_handle_nonexistent_location(self):\n        empty_worksheet = {}\n        _generate_cell_subgraph(empty_worksheet, sentinel.graph, (1, 2), set(), [])\n\n\n    @patch('sheet.dependency_graph.report_cell_error')\n    def test_should_report_then_raise_cycle_error_when_there_is_a_cycle(\n        self, mock_report_cell_error\n    ):\n        cycle_error = CycleError([(1, 2), (3, 4), (1, 2)])\n        worksheet = Worksheet()\n        worksheet[1, 2].formula = \"=foo\"\n\n        visited = set()\n        try:\n            _generate_cell_subgraph(worksheet, sentinel.graph, (1, 2), visited, [(8, 9), (1, 2), (3, 4)])\n        except Exception, e:\n            self.assertEquals(e, cycle_error)\n        else:\n            self.fail(\"No Exception raised\")\n\n        self.assertCalledOnce(mock_report_cell_error, worksheet, (1, 2), cycle_error)\n        self.assertEquals(visited, set([(1, 2)]))\n\n\n    def test_should_raise_any_existing_cycle_error_for_visited_locations(self):\n        cycle_error = CycleError([(1, 2), (3, 4), (1, 2)])\n        worksheet = Worksheet()\n        worksheet[1, 2].error = cycle_error\n\n        try:\n            _generate_cell_subgraph(worksheet, sentinel.graph, (1, 2), set([(1, 2)]), sentinel.path)\n        except Exception, e:\n            self.assertEquals(e, cycle_error)\n        else:\n            self.fail(\"No Exception raised\")\n\n\n    @patch('sheet.dependency_graph._generate_cell_subgraph')\n    @patch('sheet.dependency_graph.report_cell_error')\n    @patch('sheet.dependency_graph._add_location_dependencies', Mock())\n    def test_should_reraise_cycle_error_after_reporting_if_its_in_the_cycle_path(\n        self, mock_report_cell_error, mock_recursive_call\n    ):\n        cycle_error = CycleError([(1, 2), (3, 4), (1, 2)])\n        worksheet = Worksheet()\n        worksheet[1, 2].formula = \"=C4\"\n        mock_recursive_call.side_effect = die(cycle_error)\n\n        visited = set()\n        try:\n            _generate_cell_subgraph(worksheet, sentinel.graph, (1, 2), visited, [])\n        except Exception, e:\n            self.assertEquals(e, cycle_error)\n        else:\n            self.fail(\"No Exception raised\")\n\n        self.assertCalledOnce(mock_report_cell_error, worksheet, (1, 2), cycle_error)\n        self.assertEquals(visited, set([(1, 2)]))\n\n\n    @patch('sheet.dependency_graph._generate_cell_subgraph')\n    @patch('sheet.dependency_graph.report_cell_error')\n    @patch('sheet.dependency_graph._add_location_dependencies', Mock())\n    def test_should_not_reraise_cycle_error_if_its_outside_the_cycle_path(\n        self, mock_report_cell_error, mock_recursive_call\n    ):\n        cycle_error = CycleError([(1, 2), (3, 4), (1, 2)])\n        worksheet = Worksheet()\n        worksheet[1, 3].formula = \"=foo\"\n        mock_recursive_call.side_effect = die(cycle_error)\n\n        _generate_cell_subgraph(worksheet, sentinel.graph, (1, 3), set(), []) # should not raise\n\n\n    @patch('sheet.dependency_graph._generate_cell_subgraph')\n    def test_should_not_recurse_into_existing_cycle_errors_or_include_them_in_its_deps(\n        self, mock_recursive_call\n    ):\n        cycle_error = CycleError([(1, 2), (3, 4), (1, 2)])\n        worksheet = Worksheet()\n        worksheet[1, 2].error = cycle_error\n        worksheet[3, 4].error = cycle_error\n        worksheet[1, 3].formula = \"=foo\"\n        worksheet[1, 3].dependencies = [(3, 4)]\n\n        visited = set([(1, 2), (3, 4)])\n        graph = {}\n        _generate_cell_subgraph(worksheet, graph, (1, 3), visited, [])\n\n        dep_cell_calls = [c[0][2] for c in mock_recursive_call.call_args_list]\n        self.assertNotIn(dep_cell_calls, (3, 4))\n        self.assertEquals(visited, set([(1, 2), (1, 3), (3, 4)]))\n        self.assertEquals(graph, {(1, 3): Node((1, 3), set())})\n\n\n    def test_does_not_include_discovered_cycle_in_deps_of_current_cell(self):#\n        worksheet = Worksheet()\n        worksheet[1, 1].formula = '=A2'\n        worksheet[1, 2].formula = '=A1'\n        worksheet[1, 3].formula = '=A1'\n\n        visited = set()\n        graph = {}\n\n        _generate_cell_subgraph(worksheet, graph, (1, 3), visited, [])\n        self.assertEquals(graph, {(1, 3): Node((1, 3), set())})\n        self.assertEquals(visited, set([(1, 2), (1, 3), (1, 1)]))\n\n\n    @patch('sheet.dependency_graph.report_cell_error')\n    def test_reports_error_once_per_cell(self, mock_report_cell_error):\n        mock_report_cell_error.side_effect = report_cell_error\n        worksheet = Worksheet()\n        worksheet[1, 1].formula = '=A2'\n        worksheet[1, 2].formula = '=A1'\n\n        try:\n            _generate_cell_subgraph(worksheet, {}, (1, 1), set(), [])\n        except CycleError:\n            pass\n\n        self.assertEquals(len(mock_report_cell_error.call_args_list), 2)\n\n\nclass TestDependencyGraphNode(ResolverTestCase):\n\n    def test_constructor(self):\n        self.assertRaises(TypeError, lambda: Node())\n\n        n1 = Node((1, 2))\n        self.assertEquals(n1.location, (1, 2))\n        self.assertEquals(n1.children, set())\n        self.assertEquals(n1.parents, set())\n\n        n2 = Node((2, 3), children=set([1, 2, 3]))\n        self.assertEquals(n2.location, (2, 3))\n        self.assertEquals(n2.children, set([1, 2, 3]))\n        self.assertEquals(n2.parents, set())\n\n        n3 = Node((4, 5), parents=set([1, 2, 3]))\n        self.assertEquals(n3.location, (4, 5))\n        self.assertEquals(n3.children, set())\n        self.assertEquals(n3.parents, set([1, 2, 3]))\n\n    def test_nodes_should_have_a_lock(self):\n        node = Node((1, 2))\n        self.assertIsNotNone(node.lock.acquire)\n        self.assertIsNotNone(node.lock.release)\n\n    def test_equality(self):\n        n1 = Node((1, 2), children=set([1]))\n        n1.parents = set([2])\n        n2 = Node((1, 2), children=set([1]))\n        n2.parents = set([2])\n\n        self.assertTrue(n1 == n2)\n        self.assertFalse(n1 != n2)\n\n        n2.location = (3, 4)\n        self.assertFalse(n1 == n2)\n        self.assertTrue(n1 != n2)\n\n        n2.location = (1, 2)\n        n2.parents = set([3])\n        self.assertFalse(n1 == n2)\n        self.assertTrue(n1 != n2)\n\n        n2.children = set([3])\n        self.assertFalse(n1 == n2)\n        self.assertTrue(n1 != n2)\n\n        n2.parents = set([2])\n        self.assertFalse(n1 == n2)\n        self.assertTrue(n1 != n2)\n\n    def test_repr(self):\n        self.assertEquals(\n            str(Node((1, 2), children=set([1, 2, 3]))),\n            \"<Node 1,2 children={1, 2, 3} parents={}>\"\n        )\n\n    def test_remove_should_acquire_lock_on_parent_nodes(self):\n        parent1 = Node((1, 2))\n        parent2 = Node((2, 3))\n        node = Node((3, 4), parents=set([(1, 2), (2, 3)]))\n        parent1.children = set([(3, 4)])\n        parent2.children = set([(3, 4)])\n        leaf_queue = Mock()\n\n        parent1.lock = Mock()\n        parent1.lock.aquire.side_effect = lambda: self.assertTrue(node in parent1.children)\n        parent1.lock.release.side_effect = lambda: self.assertFalse(node in parent1.children)\n\n        parent2.lock = Mock()\n        parent2.lock.aquire.side_effect = lambda: self.assertTrue(node in parent2.children)\n        parent2.lock.release.side_effect = lambda: self.assertFalse(node in parent2.children)\n\n        node.remove_from_parents([parent1, parent2], leaf_queue)\n\n        self.assertTrue(parent1.lock.acquire.called)\n        self.assertTrue(parent1.lock.release.called)\n        self.assertTrue(parent2.lock.acquire.called)\n        self.assertTrue(parent2.lock.release.called)\n\n        self.assertEquals(\n            leaf_queue.put.call_args_list,\n            [call((1, 2)), call((2, 3))]\n        )\n\n\n    def test_remove_should_add_new_leaves_to_queue(self):\n        parent = Node((1, 2))\n        child1 = Node((2, 3), parents=set([parent.location]))\n        child2 = Node((3, 4), parents=set([parent.location]))\n        parent.children = set([child1.location, child2.location])\n        leaf_queue = Mock()\n\n        child1.remove_from_parents([parent], leaf_queue)\n\n        self.assertFalse(leaf_queue.put.called)\n\n        child2.remove_from_parents([parent], leaf_queue)\n\n        self.assertEquals(leaf_queue.put.call_args, ((parent.location,), {}))\n\n\nclass TestAddLocationDependencies(ResolverTestCase):\n\n    def test_add_location_dependencies_does(self):\n        graph = {}\n        dependencies = set([sentinel.dependencies])\n        _add_location_dependencies(graph, sentinel.location, dependencies)\n        self.assertEquals(type(graph[sentinel.location]), Node)\n        self.assertEquals(graph[sentinel.location].children, dependencies)\n\n    def test_add_location_dependencies_also_adds_reverse_dependencies(self):\n        graph = {}\n        parent_loc = (1, 2)\n        child1_loc = (2, 3)\n        child2_loc = (3, 4)\n        grandchild_loc = (4, 5)\n\n        _add_location_dependencies(graph, parent_loc, set([child1_loc, child2_loc]))\n        expected = {\n            parent_loc: Node(parent_loc, children=set([child1_loc, child2_loc])),\n            child1_loc: Node(child1_loc, parents=set([parent_loc])),\n            child2_loc: Node(child2_loc, parents=set([parent_loc])),\n        }\n        self.assertEquals(expected, graph)\n\n        _add_location_dependencies(graph, grandchild_loc, set())\n        expected = {\n            parent_loc: Node(parent_loc, children=set([child1_loc, child2_loc])),\n            child1_loc: Node(child1_loc, parents=set([parent_loc])),\n            child2_loc: Node(child2_loc, parents=set([parent_loc])),\n            grandchild_loc: Node(grandchild_loc),\n        }\n        self.assertEquals(expected, graph)\n\n        _add_location_dependencies(graph, child1_loc, set([grandchild_loc]))\n        expected = {\n            parent_loc: Node(parent_loc, children=set([child1_loc, child2_loc])),\n            child1_loc: Node(\n                child1_loc,\n                children=set([grandchild_loc]),\n                parents=set([parent_loc])\n            ),\n            child2_loc: Node(child2_loc, parents=set([parent_loc])),\n            grandchild_loc: Node(grandchild_loc, parents=set([child1_loc])),\n        }\n        self.assertEquals(expected, graph)\n\n"
  },
  {
    "path": "dirigible/sheet/tests/test_dirigible_datetime.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\ntry:\n    import unittest2 as unittest\nexcept ImportError:\n    import unittest\n\nimport datetime\nfrom sheet.dirigible_datetime import DateTime\nfrom dirigible.test_utils import ResolverTestCase\n\nclass DateTimeTest(ResolverTestCase):\n\n    def test_DateTime_subclasses_datetime_dot_datetime(self):\n        self.assertTrue(isinstance(\n            DateTime(1979, 10, 8),\n            datetime.datetime))\n"
  },
  {
    "path": "dirigible/sheet/tests/test_errors.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\nfrom textwrap import dedent\n\ntry:\n    import unittest2 as unittest\nexcept ImportError:\n    import unittest\n\nfrom mock import sentinel, Mock\n\nfrom sheet.cell import undefined\nfrom sheet.errors import CycleError, report_cell_error\nfrom sheet.worksheet import Worksheet\n\n\nclass TestCycleError(unittest.TestCase):\n\n    def test_cycle_error_str_reports_path(self):\n        ce1 = CycleError([(1, 2), (4, 5), (1, 2)])\n        self.assertEquals(str(ce1), 'A2 -> D5 -> A2')\n\n\n    def test_cycle_error_repr_reports_path(self):\n        ce1 = CycleError([(1, 2), (4, 5), (1, 2)])\n        self.assertEquals(repr(ce1), 'CycleError(A2 -> D5 -> A2)')\n\n\n    def test_cycle_errors_compare_unequal_to_random_crap(self):\n        ce1 = CycleError([(1, 2), (4, 5), (1, 2)])\n        self.assertFalse(ce1 == object())\n        self.assertTrue(ce1 != object())\n\n\n    def test_cycle_errors_compare_equal_from_identical_path(self):\n        ce1 = CycleError([(1, 2), (4, 5), (1, 2)])\n        ce2 = CycleError([(1, 2), (4, 5), (1, 2)])\n        self.assertTrue(ce1 == ce2)\n        self.assertFalse(ce1 != ce2)\n\n\n    def test_cycle_errors_compare_unequal_from_different_paths(self):\n        ce1 = CycleError([(1, 2), (4, 5), (1, 2)])\n        ce2 = CycleError([(1, 2), (4, 6), (1, 2)])\n        self.assertFalse(ce1 == ce2)\n        self.assertTrue(ce1 != ce2)\n\n\nclass TestReportCellError(unittest.TestCase):\n\n    def test_report_cell_error(self):\n        worksheet = Worksheet()\n        worksheet.add_console_text = Mock()\n        worksheet[1, 2].formula = '=A1'\n\n        report_cell_error(worksheet, (1, 2), ZeroDivisionError('hello'))\n\n        self.assertEqual(\n            worksheet[(1, 2)].error,\n            'ZeroDivisionError: hello'\n            )\n        self.assertEqual(\n            worksheet[(1, 2)].value,\n            undefined\n            )\n        expected_error_text = dedent('''\n            ZeroDivisionError: hello\n                Formula '=A1' in A2\n            ''')[1:]\n        self.assertEqual(\n            worksheet.add_console_text.call_args_list,\n            [((expected_error_text,), {})],\n            )\n\n\n"
  },
  {
    "path": "dirigible/sheet/tests/test_eval_constant.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\r\n# See LICENSE.md\r\n#\r\n\r\ntry:\r\n    import unittest2 as unittest\r\nexcept ImportError:\r\n    import unittest\r\n\r\nfrom sheet.eval_constant import eval_constant\r\n\r\n\r\nclass TestEvalConstant(unittest.TestCase):\r\n\r\n    def test_returns_input_unchanged_in_general(self):\r\n        input = 'input'\r\n        self.assertEquals(id(eval_constant(input)), id(input))\r\n\r\n\r\n    def test_returns_float_for_floatlike_input(self):\r\n        self.assertEquals(eval_constant('1'), 1)\r\n        self.assertEquals(type(eval_constant('1')), type(1))\r\n        self.assertEquals(eval_constant('1.5'), 1.5)\r\n        self.assertEquals(eval_constant('1.5e10'), 1.5e10)\r\n        self.assertEquals(type(eval_constant('1.5')), type(1.5))\r\n\r\n"
  },
  {
    "path": "dirigible/sheet/tests/test_forms.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\nfrom django import forms\n\nfrom dirigible.test_utils import ResolverTestCase\nfrom sheet.forms import ImportCSVForm\n\nclass TestImportCSVForm(ResolverTestCase):\n\n    def test_initialisation(self):\n        import_form = ImportCSVForm()\n        self.assertTrue(all(map(lambda args : isinstance(*args),[\n            (import_form.fields['column'], forms.IntegerField),\n            (import_form.fields['row'], forms.IntegerField),\n            (import_form.fields['file'], forms.FileField)])))\n\n\n    def test_hidden_fields_appear_with_correct_ids(self):\n        import_form = ImportCSVForm()\n        autogen_html = import_form.as_p()\n\n        first_hidden_position = autogen_html.find('type=\"hidden\"')\n        self.assertFalse(first_hidden_position == -1)\n        second_hidden_position = autogen_html.find('type=\"hidden\"', first_hidden_position + 1)\n        self.assertFalse(second_hidden_position == -1)\n        third_hidden_position = autogen_html.find('type=\"hidden\"', second_hidden_position + 1)\n        self.assertEquals(third_hidden_position, -1)\n\n        self.assertTrue('id=\"id_import_form_column\"' in autogen_html)\n        self.assertTrue('id=\"id_import_form_row\"' in autogen_html)\n        self.assertTrue('id=\"id_import_form_file\"' in autogen_html)\n"
  },
  {
    "path": "dirigible/sheet/tests/test_formula_interpreter.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\nfrom __future__ import with_statement\n\ntry:\n    import unittest2 as unittest\nexcept ImportError:\n    import unittest\n\nfrom mock import Mock, patch, sentinel\n\nfrom sheet.formula_interpreter import (\n    get_dependencies_from_parse_tree, get_python_formula_from_parse_tree,\n    rewrite\n)\nfrom sheet.parser import FormulaError\nfrom sheet.parser.parse_node import ParseNode\nfrom sheet.parser.parse_node_constructors import FLCellReference\nfrom sheet.parser.parser import parse\nfrom dirigible.test_utils import ResolverTestCase\n\n\nclass TestGetPythonFormulaFromParseTree(ResolverTestCase):\n\n    @patch('sheet.formula_interpreter.rewrite')\n    def test_flattens_rewrites_and_removes_1st_char(self, mock_rewrite):\n        mock_rewrite.return_value = Mock()\n        mock_rewrite.return_value.flatten.return_value = '123'\n\n        result = get_python_formula_from_parse_tree(sentinel.parsed_formula)\n\n        self.assertCalledOnce(mock_rewrite, sentinel.parsed_formula)\n        self.assertCalledOnce(mock_rewrite.return_value.flatten)\n        self.assertEquals(result, '23')\n\n\n    def test_converts_formula_starting_with_equals(self):\n        self.assertEquals(get_python_formula_from_parse_tree(parse('=1')), \"1\")\n        self.assertEquals(get_python_formula_from_parse_tree(parse('=1+2')), \"1+2\")\n\n\n    def test_converts_cell_references_and_adds_space(self):\n        self.assertEquals(\n            get_python_formula_from_parse_tree(parse('=A1')),\n            \"worksheet[(1,1)].value \"\n        )\n\n\n    def test_produces_correct_python(self):\n        self.assertEquals(\n            get_python_formula_from_parse_tree(parse(\n                '=[x * A1 for x in range(5)]'\n            )),\n            '[x * worksheet[(1,1)].value for x in range(5)]'\n        )\n        self.assertEquals(\n            get_python_formula_from_parse_tree(parse(\n                '=[x in A1:B3 for x in range(5)]'\n            )),\n            '[x in CellRange(worksheet,(1,1),(2,3)) for x in range(5)]'\n        )\n        self.assertEquals(\n            get_python_formula_from_parse_tree(parse('={1 -> B1}')),\n            '{1 :worksheet[(2,1)].value }'\n        )\n        self.assertEquals(\n            get_python_formula_from_parse_tree(parse('=(lambda x -> C1 * x)(2)')),\n            '(lambda x :worksheet[(3,1)].value * x)(2)'\n        )\n\n\n\nclass TestRewrite(ResolverTestCase):\n\n    def test_slicing_in_formulae(self):\n        self.assertEquals(\n            rewrite(parse('=p[1->]')).flatten(),\n            '=p[1:]'\n        )\n        self.assertEquals(\n            rewrite(parse('=p[->6]')).flatten(),\n            '=p[:6]'\n        )\n        self.assertEquals(\n            rewrite(parse('=p[1->6]')).flatten(),\n            '=p[1:6]'\n        )\n        self.assertEquals(\n            rewrite(parse('=p[1->6->3]')).flatten(),\n            '=p[1:6:3]'\n        )\n        self.assertEquals(\n            rewrite(parse('=p[1->->3]')).flatten(),\n            '=p[1::3]'\n        )\n\n\n    def test_rewrite_string_should_return_object_unchanged(self):\n        input = \"Hello, world\"\n        self.assertEquals(rewrite(input), input)\n\n\n    @patch('sheet.formula_interpreter.rewrite')\n    def test_rewrite_parse_node_should_return_parse_node_with_children_rewritten(self, mock_recursive_rewrite):\n        node_type = \"junk node type\"\n        children = [ParseNode(node_type, None), ParseNode(node_type, None)]\n        input = ParseNode(node_type, children)\n\n        mock_recursive_results = [object(), object()]\n        def mock_recursive_results_generator_func(*args):\n            yield mock_recursive_results[0]\n            yield mock_recursive_results[1]\n        mock_recursive_results_generator = mock_recursive_results_generator_func()\n        mock_recursive_rewrite.side_effect = lambda *_: mock_recursive_results_generator.next()\n\n        self.assertEquals(id(rewrite(input)), id(input))\n        self.assertEquals(\n            mock_recursive_rewrite.call_args_list,\n            [\n                ((children[0],), {}),\n                ((children[1],), {})\n            ]\n        )\n        self.assertEquals(\n            input.children,\n            mock_recursive_results\n        )\n\n\n    def test_rewrite_cell_reference_should_return_appropriate_tree(self):\n        self.assertEquals(\n            rewrite(FLCellReference([\"A2\"])).flatten(),\n            'worksheet[(1,2)].value '\n        )\n\n\n    def test_rewrite_should_translate_lambda_arrow_to_colon(self):\n        self.assertEquals(\n            rewrite(parse('=lambda x->  x')).flatten(),\n            '=lambda x:x'\n        )\n\n        self.assertEquals(\n            rewrite(parse('=lambda   x-> x')).flatten(),\n            '=lambda   x:x'\n            )\n\n        self.assertEquals(\n            rewrite(parse('=lambda x  -> x')).flatten(),\n            '=lambda x  :x'\n            )\n\n        self.assertEquals(\n            rewrite(parse('=lambda -> x')).flatten(),\n            '=lambda :x'\n            )\n\n\n    def test_rewrite_should_translate_dictionary_arrow_to_colon(self):\n        self.assertEquals(\n            rewrite(parse('={\"key\"->\"value\"}')).flatten(),\n            '={\"key\":\"value\"}'\n            )\n\n        self.assertEquals(\n            rewrite(parse('={\"key\"  ->\"value\"}')).flatten(),\n            '={\"key\"  :\"value\"}'\n            )\n\n        self.assertEquals(\n            rewrite(parse('={\"key\"->  \"value\"}')).flatten(),\n            '={\"key\":\"value\"}'\n            )\n\n\n    def test_rewrite_should_raise_invalid_cell_reference_error_when_appropriate(self):\n        with self.assertRaises(FormulaError) as mgr:\n            rewrite(parse('=#Invalid!')).flatten()\n        self.assertEquals(str(mgr.exception), \"#Invalid! cell reference in formula\")\n\n        with self.assertRaises(FormulaError) as mgr:\n            rewrite(parse('=sum(#Invalid!:B1)'))\n        self.assertEquals(str(mgr.exception), \"#Invalid! cell reference in formula\")\n\n\n    def test_rewrite_should_raise_deleted_cell_reference_error_when_appropriate(self):\n        with self.assertRaises(FormulaError) as mgr:\n            rewrite(parse('=#Deleted!')).flatten()\n        self.assertEquals(str(mgr.exception), \"#Deleted! cell reference in formula\")\n\n        with self.assertRaises(FormulaError) as mgr:\n            rewrite(parse('=sum(#Deleted!:B1)'))\n        self.assertEquals(str(mgr.exception), \"#Deleted! cell reference in formula\")\n\n\n\nclass TestGetParseTreeDependencies(ResolverTestCase):\n\n    def test_get_parse_tree_dependencies_should_return_empty_list_when_no_cell_refs(self):\n        tree = parse(\"=1+2\")\n        self.assertEquals(get_dependencies_from_parse_tree(tree), [])\n\n\n    def test_get_parse_tree_dependencies_should_return_locations_for_simple_expression(self):\n        tree = parse(\"=a1+a2\")\n        self.assertEquals(get_dependencies_from_parse_tree(tree), [(1, 1), (1, 2)])\n\n\n    def test_get_parse_tree_dependencies_should_return_locations_for_simple_expression_with_case_mismatch(self):\n        tree = parse(\"=a1+A2\")\n        self.assertEquals(get_dependencies_from_parse_tree(tree), [(1, 1), (1, 2)])\n\n\n    def test_get_parse_tree_dependencies_should_return_locations_disregarding_worksheet_names(self):\n        tree = parse(\"=Sheet1!A3\")\n        self.assertEquals(get_dependencies_from_parse_tree(tree), [(1, 3)])\n\n\n    def test_get_parse_tree_dependencies_should_return_locations_used_for_function_calls_and_arguments(self):\n        tree = parse(\"=A2(a3)\")\n        self.assertEquals(get_dependencies_from_parse_tree(tree), [(1, 2), (1, 3)])\n\n\n    def test_get_parse_tree_dependencies_should_not_return_locations_for_invalid_references(self):\n        self.assertEquals(get_dependencies_from_parse_tree(parse(\"=#Invalid!\")), [])\n        self.assertEquals(get_dependencies_from_parse_tree(parse(\"=#Invalid! + A2\")), [(1, 2)])\n\n\n    def test_get_parse_tree_dependencies_should_not_return_locations_for_deleted_references(self):\n        self.assertEquals(get_dependencies_from_parse_tree(parse(\"=#Deleted!\")), [])\n        self.assertEquals(get_dependencies_from_parse_tree(parse(\"=#Deleted! + A2\")), [(1, 2)])\n\n\n    def test_get_parse_tree_dependencies_should_not_return_locations_for_invalid_references_in_cell_ranges(self):\n        self.assertEquals(get_dependencies_from_parse_tree(parse(\"=#Invalid!:A2\")), [])\n        self.assertEquals(get_dependencies_from_parse_tree(parse(\"=A2:#Invalid!\")), [])\n        self.assertEquals(get_dependencies_from_parse_tree(parse(\"=#Invalid!:#Invalid!\")), [])\n\n\n    def test_get_parse_tree_dependencies_should_not_return_locations_for_deleted_references_in_cell_ranges(self):\n        self.assertEquals(get_dependencies_from_parse_tree(parse(\"=#Deleted!:A2\")), [])\n        self.assertEquals(get_dependencies_from_parse_tree(parse(\"=A2:#Deleted!\")), [])\n        self.assertEquals(get_dependencies_from_parse_tree(parse(\"=#Deleted!:#Deleted!\")), [])\n\n\n    def test_get_parse_tree_dependencies_should_not_return_locations_for_worksheet_references(self):\n        for expression in (\"=<Sheet1>\", \"=<Sheet1>.Bounds\", \"=<Sheet1> + <Sheet2>\"):\n            self.assertEquals(get_dependencies_from_parse_tree(parse(expression)), [])\n        self.assertEquals(get_dependencies_from_parse_tree(parse(\"=<Sheet1> + A2\")), [(1, 2)])\n\n\n    def test_get_parse_tree_dependencies_should_return_cellrange_deps(self):\n        self.assertItemsEqual(\n            get_dependencies_from_parse_tree(parse('=a2:b3')),\n            [(1, 2), (1, 3), (2, 2), (2, 3)]\n        )\n        self.assertItemsEqual(\n            get_dependencies_from_parse_tree(parse('=b3:a2')),\n            [(1, 2), (1, 3), (2, 2), (2, 3)]\n        )\n        self.assertItemsEqual(\n            get_dependencies_from_parse_tree(parse('=a3:b2')),\n            [(1, 2), (1, 3), (2, 2), (2, 3)]\n        )\n\n\n"
  },
  {
    "path": "dirigible/sheet/tests/test_importer.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\nfrom __future__ import with_statement\n\ntry:\n    import unittest2 as unittest\nexcept ImportError:\n    import unittest\n\nfrom mock import Mock, patch\nfrom StringIO import StringIO\nimport xlrd\n\nfrom dirigible.test_utils import ResolverTestCase\nfrom sheet.importer import (\n        DirigibleImportError, worksheet_from_csv, worksheet_from_excel\n)\nfrom sheet.worksheet import Worksheet\n\n\n\nclass WorksheetFromCSVTest(ResolverTestCase):\n\n    def test_should_put_data_into_existing_worksheet_with_offset_for_excel_and_auto(self):\n        for excel_encoding in [True, False]:\n            csv = StringIO()\n            csv.write('abc,123\\n')\n            csv.write('def, \\n')\n            csv.size = 10\n            csv.seek(0)\n\n            existing_worksheet = Worksheet()\n            for row in range(1, 6):\n                for col in range(1, 5):\n                    existing_worksheet[col, row].formula = 'old'\n\n            worksheet = worksheet_from_csv(existing_worksheet, csv, 2, 3, excel_encoding)\n\n            self.assertEquals(worksheet.A1.formula, 'old')\n            self.assertEquals(worksheet.B1.formula, 'old')\n            self.assertEquals(worksheet.A2.formula, 'old')\n            self.assertEquals(worksheet.B3.formula, 'abc')\n            self.assertEquals(worksheet.C3.formula, '123')\n            self.assertEquals(worksheet.B4.formula, 'def')\n            self.assertEquals(worksheet.C4.formula, ' ')\n            self.assertEquals(worksheet.C5.formula, 'old')\n            self.assertEquals(worksheet.D3.formula, 'old')\n            self.assertEquals(worksheet.B5.formula, 'old')\n\n\n    def test_excel_csv_import_recognises_accents_and_currency_symbols(self):\n        excel_csv = StringIO()\n        excel_csv.write(u\"\\xe9\".encode('windows-1252'))\n        excel_csv.write(u\"\\xa3\".encode('windows-1252'))\n        excel_csv.write(u\"\\u20ac\".encode('windows-1252'))\n        excel_csv.name = 'filename'\n        excel_csv.size = 10\n        excel_csv.seek(0)\n\n        worksheet = worksheet_from_csv(Worksheet(), excel_csv, 3, 4, True)\n\n        self.assertEquals(worksheet.C4.formula, u\"\\xe9\\xa3\\u20ac\")\n\n\n\n    def test_excel_csv_import_handles_carriage_returns_in_cells(self):\n        excel_csv = StringIO()\n        excel_csv.write(u'\"carriage\\nreturn!\"\\r\\n'.encode('windows-1252'))\n        excel_csv.write(u\"normal line\\r\\n\".encode('windows-1252'))\n        excel_csv.name = 'filename'\n        excel_csv.size = 10\n        excel_csv.seek(0)\n\n        worksheet = worksheet_from_csv(Worksheet(), excel_csv, 2, 3, True)\n\n        self.assertEquals(worksheet.B3.formula, \"carriage\\nreturn!\")\n        self.assertEquals(worksheet.B4.formula, \"normal line\")\n\n\n    def test_autodetect_csv_import_handles_carriage_returns_in_cells(self):\n        excel_csv = StringIO()\n        excel_csv.write(u'\"carriage\\nreturn!\"\\r\\n'.encode('utf-8'))\n        excel_csv.write(u\"normal line\\r\\n\".encode('utf-8'))\n        excel_csv.name = 'filename'\n        excel_csv.size = 10\n        excel_csv.seek(0)\n\n        worksheet = worksheet_from_csv(Worksheet(), excel_csv, 2, 3, False)\n\n        self.assertEquals(worksheet.B3.formula, \"carriage\\nreturn!\")\n        self.assertEquals(worksheet.B4.formula, \"normal line\")\n\n\n    def test_autodetect_can_handle_japanese_utf8(self):\n        some_kanji = u'\\u65b0\\u4e16\\u7d00\\u30a8\\u30f4\\u30a1\\u30f3\\u30b2\\u30ea\\u30aa\\u30f3'\n        japanese_file = StringIO()\n        japanese_file.write(some_kanji.encode('utf-8'))\n        japanese_file.name = 'filename'\n        japanese_file.size = 10\n        japanese_file.seek(0)\n\n        worksheet = worksheet_from_csv(Worksheet(), japanese_file, 1, 1, False)\n\n        self.assertEquals(worksheet.A1.formula, some_kanji)\n\n\n    def test_excel_csv_import_survives_japanes_utf8(self):\n        some_kanji = u'\\u65b0\\u4e16\\u7d00\\u30a8\\u30f4\\u30a1\\u30f3\\u30b2\\u30ea\\u30aa\\u30f3'\n        japanese_file = StringIO()\n        japanese_file.write(some_kanji.encode('utf-8'))\n        japanese_file.name = 'filename'\n        japanese_file.size = 10\n        japanese_file.seek(0)\n\n        worksheet_from_csv(Worksheet(), japanese_file, 1, 1, True)\n        #should not raise\n\n\n    def test_import_excel_csv_raises_on_null_bytes(self):\n        bin_file = StringIO()\n        bin_file.write(\"\\xFF\\x00\\xFF\")\n        bin_file.name = 'filename'\n        bin_file.size = 10\n        bin_file.seek(0)\n\n        self.assertRaises(DirigibleImportError,\n                lambda : worksheet_from_csv(Worksheet(), bin_file, 2, 1, True)\n        )\n\n    def test_autodetect_import_csv_raises_on_null_bytes(self):\n        bin_file = StringIO()\n        bin_file.write(\"\\xFF\\x00\\xFF\")\n        bin_file.name = 'filename'\n        bin_file.size = 10\n        bin_file.seek(0)\n\n        self.assertRaises(DirigibleImportError,\n                lambda : worksheet_from_csv(Worksheet(), bin_file, 1, 1, False)\n        )\n\n\n    @patch('sheet.importer.UniversalDetector')\n    def test_autodetect_import_csv_raises_on_failure_to_detect_encoding(\n            self, mock_UniversalDetector\n    ):\n        mock_detector = Mock()\n        mock_UniversalDetector.return_value = mock_detector\n        mock_detector.result = {'encoding':None}\n\n        mock_file = StringIO()\n        mock_file.write(\"\\xFF\\x00\\xFF\")\n        mock_file.name = 'filename'\n        mock_file.size = 10\n\n        self.assertRaises(DirigibleImportError,\n                lambda : worksheet_from_csv(Worksheet(), mock_file, 1, 1, False)\n        )\n\n\n\nclass WorksheetFromExcelTest(ResolverTestCase):\n\n    def test_populates_worksheet_formulae_from_excel_values(self):\n        mock_excel_worksheet = Mock()\n        def mock_cell(row, col):\n            mock_cell = Mock()\n            mock_cell.value = '%s, %s' % (col, row)\n            return mock_cell\n        mock_excel_worksheet.cell.side_effect = mock_cell\n        mock_excel_worksheet.nrows = 4\n        mock_excel_worksheet.ncols = 3\n\n        worksheet = worksheet_from_excel(mock_excel_worksheet)\n\n        for col in range(mock_excel_worksheet.ncols):\n            for row in range(mock_excel_worksheet.nrows):\n                self.assertEquals(worksheet[col + 1, row + 1].formula, '%s, %s' % (col, row))\n\n\n    def test_populates_worksheet_handles_float_source_values(self):\n        mock_excel_worksheet = Mock()\n        def mock_cell(row, col):\n            mock_cell = Mock()\n            mock_cell.value = col + row + 0.1\n            return mock_cell\n        mock_excel_worksheet.cell.side_effect = mock_cell\n        mock_excel_worksheet.nrows = 4\n        mock_excel_worksheet.ncols = 3\n\n        worksheet = worksheet_from_excel(mock_excel_worksheet)\n\n        for col in range(mock_excel_worksheet.ncols):\n            for row in range(mock_excel_worksheet.nrows):\n                self.assertEquals(worksheet[col + 1, row + 1].formula, '%s' % (col + row + 0.1, ))\n\n\n    @patch('sheet.importer.xldate_as_tuple')\n    def test_converts_excel_dates_to_python_datetime(self, mock_xlrd_date_as_tuple):\n        mock_excel_worksheet = Mock()\n        def mock_cell(row, col):\n            mock_cell = Mock()\n            mock_cell.ctype = xlrd.XL_CELL_DATE\n            mock_cell.value = (row, col)\n            return mock_cell\n        mock_excel_worksheet.cell.side_effect = mock_cell\n        mock_excel_worksheet.nrows = 4\n        mock_excel_worksheet.ncols = 3\n\n        def mock_xlrd_date_as_tuple_function(cell_value, datemode):\n            row, col = cell_value\n            self.assertEquals(datemode, mock_excel_worksheet.book.datemode)\n            return (2011, row, col, 1, 2, 3)\n        mock_xlrd_date_as_tuple.side_effect = mock_xlrd_date_as_tuple_function\n\n        worksheet = worksheet_from_excel(mock_excel_worksheet)\n\n        for col in range(mock_excel_worksheet.ncols):\n            for row in range(mock_excel_worksheet.nrows):\n                self.assertEquals(\n                        worksheet[col + 1, row + 1].formula,\n                        '=DateTime(2011, %s, %s, 1, 2, 3)' % (row, col)\n                )\n\n\n    @patch('sheet.importer.xldate_as_tuple')\n    def test_handles_excel_errors(self, mock_xlrd_date_as_tuple):\n        mock_excel_worksheet = Mock()\n        errors = {\n                (0,0) : (0x0, '=#NULL!'),\n                (1,0) : (0x7, '=#DIV/0!'),\n                (2,0) : (0xf, '=#VALUE!'),\n                (3,0) : (0x17, '=#REF!'),\n                (4,0) : (0x1d, '=#NAME?'),\n                (5,0) : (0x24, '=#NUM!'),\n                (6,0) : (0x2a, '=#N/A'),\n\n        }\n        def mock_cell(row, col):\n            mock_cell = Mock()\n            mock_cell.ctype = xlrd.XL_CELL_ERROR\n            mock_cell.value = errors[row, col][0]\n            return mock_cell\n        mock_excel_worksheet.cell.side_effect = mock_cell\n        mock_excel_worksheet.nrows = 7\n        mock_excel_worksheet.ncols = 1\n\n        worksheet = worksheet_from_excel(mock_excel_worksheet)\n\n        for col in range(mock_excel_worksheet.ncols):\n            for row in range(mock_excel_worksheet.nrows):\n                self.assertEquals(\n                        worksheet[col + 1, row + 1].formula,\n                        errors[row, col][1]\n                )\n\n"
  },
  {
    "path": "dirigible/sheet/tests/test_rewrite_formula_offset_cell_references.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\ntry:\n    import unittest2 as unittest\nexcept ImportError:\n    import unittest\n\nfrom sheet.worksheet import Worksheet\nfrom sheet.rewrite_formula_offset_cell_references import (\n    rewrite_formula, rewrite_source_sheet_formulae_for_cut,\n)\n\nclass TestRewriteFormulaOffsetCellReferences(unittest.TestCase):\n\n    def test_dont_rewrite_constants(self):\n        result = rewrite_formula(\n            \"B3\", 3, 5, False, (1, 2, 3, 4)\n        )\n        self.assertEquals(result, 'B3')\n\n\n    def test_safely_handle_none(self):\n        self.assertIsNone( rewrite_formula(None, 3, 5, False, (1, 2, 3, 4)) )\n\n\n    def test_safely_handle_nonsense(self):\n        unparseable_nonsense = '=!:booA1:A2'\n        self.assertEquals(\n                rewrite_formula(unparseable_nonsense, 3, 5, False, (1, 2, 3, 4)),\n                unparseable_nonsense\n        )\n\n\n    def test_cut_cell_reference_to_cut_cell_is_rewritten(self):\n        result = rewrite_formula(\n            \"=A2\", 2, 1, True, (1, 1, 1, 2)\n        )\n        self.assertEquals(result, '=C3')\n\n\n    def test_cut_cell_reference_to_uncut_cell_is_not_rewritten(self):\n        result = rewrite_formula(\n            \"=B3\", 2, 1, True, (1, 1, 1, 1)\n        )\n        self.assertEquals(result, '=B3')\n\n\n    def test_absolute_cut_cell_reference_to_uncut_cell_is_not_rewritten(self):\n        result = rewrite_formula(\n            \"=$B$3\", 2, 1, True, (1, 1, 1, 1)\n        )\n        self.assertEquals(result, '=$B$3')\n\n\n    def test_absolute_cut_cell_reference_to_cut_cell_is_rewritten(self):\n        result = rewrite_formula(\n            \"=$A$2\", 2, 1, True, (1, 1, 1, 2)\n        )\n        self.assertEquals(result, '=$C$3')\n\n\n    def test_copied_cell_reference_to_copied_cell_is_rewritten(self):\n        result = rewrite_formula(\n            \"=A2\", 2, 1, False, (1, 1, 1, 2)\n        )\n        self.assertEquals(result, '=C3')\n\n\n    def test_copied_cell_reference_to_uncopied_cell_is_rewritten(self):\n        result = rewrite_formula(\n            \"=B3\", 2, 1, False, (1, 1, 1, 1)\n        )\n        self.assertEquals(result, '=D4')\n\n\n    def test_absolute_copied_cell_reference_to_copied_cell_is_not_rewritten(self):\n        result = rewrite_formula(\n            \"=$A$2\", 2, 1, False, (1, 1, 1, 2)\n        )\n        self.assertEquals(result, '=$A$2')\n\n\n    def test_absolute_copied_cell_reference_to_uncopied_cell_is_not_rewritten(self):\n        result = rewrite_formula(\n            \"=$B$3\", 2, 1, False, (1, 1, 1, 1)\n        )\n        self.assertEquals(result, '=$B$3')\n\n\n    def test_copied_cell_reference_that_moves_off_grid_marked_invalid(self):\n        result = rewrite_formula(\n            \"=A1\", 1, -1, False, (1, 2, 1, 2)\n        )\n        self.assertEquals(result, '=#Invalid!')\n\n\n    def test_cut_cellrange_reference_to_completely_cut_cellrange_is_rewritten(self):\n        result = rewrite_formula(\n            \"=A2:A3\", 2, 1, True, (1, 1, 1, 3)\n        )\n        self.assertEquals(result, '=C3:C4')\n\n\n    def test_cut_cellrange_reference_to_partially_cut_cellrange_is_not_rewritten(self):\n        result = rewrite_formula(\n            \"=A2:A3\", 2, 1, True, (1, 1, 1, 2)\n        )\n        self.assertEquals(result, '=A2:A3')\n\n\n    def test_cut_absolute_cellrange_reference_to_completely_cut_cellrange_is_rewritten(self):\n        result = rewrite_formula(\n            \"=$A$2:$A$3\", 2, 1, True, (1, 1, 1, 3)\n        )\n        self.assertEquals(result, '=$C$3:$C$4')\n\n\n    def test_cut_absolute_cellrange_reference_to_partially_cut_cellrange_is_not_rewritten(self):\n        result = rewrite_formula(\n            \"=$A$2:$A$3\", 2, 1, True, (1, 1, 1, 2)\n        )\n        self.assertEquals(result, '=$A$2:$A$3')\n\n\n    def test_cut_cellrange_reference_to_partially_cut_cellrange_is_not_rewritten_even_if_its_not_obviously_overlapping(self):\n        cut_region_left = 2\n        cut_region_right = 3\n        cut_region_top = 1\n        cut_region_bottom = 2\n\n        cell_range_topleft = \"A2\"\n        cell_range_bottomright = \"B3\"\n\n        result = rewrite_formula(\n            \"=%s:%s\" % (cell_range_topleft, cell_range_bottomright),\n            2, 1,\n            True,\n            (cut_region_left, cut_region_top, cut_region_right, cut_region_bottom)\n        )\n        self.assertEquals(result, '=A2:B3')\n\n\n    def test_cut_absolute_cellrange_reference_to_partially_cut_cellrange_is_not_rewritten_even_if_its_not_obviously_overlapping(self):\n        cut_region_left = 2\n        cut_region_right = 3\n        cut_region_top = 1\n        cut_region_bottom = 2\n\n        cell_range_topleft = \"$A$2\"\n        cell_range_bottomright = \"$B$3\"\n\n        result = rewrite_formula(\n            \"=%s:%s\" % (cell_range_topleft, cell_range_bottomright),\n            2, 1,\n            True,\n            (cut_region_left, cut_region_top, cut_region_right, cut_region_bottom)\n        )\n        self.assertEquals(result, '=$A$2:$B$3')\n\n\n    def test_cut_cellrange_reference_to_uncut_cellrange_is_not_rewritten(self):\n        result = rewrite_formula(\n            \"=A2:A3\", 2, 1, True, (1, 1, 1, 1)\n        )\n        self.assertEquals(result, '=A2:A3')\n\n\n    def test_cut_absolute_cellrange_reference_to_uncut_cellrange_is_not_rewritten(self):\n        result = rewrite_formula(\n            \"=$A$2:$A$3\", 2, 1, True, (1, 1, 1, 1)\n        )\n        self.assertEquals(result, '=$A$2:$A$3')\n\n\n    def test_copied_cellrange_reference_to_completely_copied_cellrange_is_rewritten(self):\n        result = rewrite_formula(\n            \"=A2:A3\", 2, 1, False, (1, 1, 1, 3)\n        )\n        self.assertEquals(result, '=C3:C4')\n\n\n    def test_copied_absolute_cellrange_reference_to_completely_copied_cellrange_is_not_rewritten(self):\n        result = rewrite_formula(\n            \"=$A$2:$A$3\", 2, 1, False, (1, 1, 1, 3)\n        )\n        self.assertEquals(result, '=$A$2:$A$3')\n\n\n    def test_copied_cellrange_reference_to_partially_copied_cellrange_is_rewritten(self):\n        result = rewrite_formula(\n            \"=A2:A3\", 2, 1, False, (1, 1, 1, 2)\n        )\n        self.assertEquals(result, '=C3:C4')\n\n\n    def test_copied_absolute_cellrange_reference_to_partially_copied_cellrange_is_not_rewritten(self):\n        result = rewrite_formula(\n            \"=$A$2:$A$3\", 2, 1, False, (1, 1, 1, 2)\n        )\n        self.assertEquals(result, '=$A$2:$A$3')\n\n\n    def test_copied_cellrange_reference_to_uncopied_cellrange_is_rewritten(self):\n        result = rewrite_formula(\n            \"=A2:A3\", 2, 1, False, (1, 1, 1, 1)\n        )\n        self.assertEquals(result, '=C3:C4')\n\n\n    def test_copied_absolute_cellrange_reference_to_uncopied_cellrange_is_not_rewritten(self):\n        result = rewrite_formula(\n            \"=$A$2:$A$3\", 2, 1, False, (1, 1, 1, 1)\n        )\n        self.assertEquals(result, '=$A$2:$A$3')\n\n\n    def test_copied_cellrange_reference_that_moves_off_grid_marked_invalid(self):\n        result = rewrite_formula(\n            \"=A1:A2\", 1, -1, False, (1, 3, 1, 3)\n        )\n        self.assertEquals(result, '=#Invalid!:B1')\n\n\n    def test_source_sheet_cell_references_to_cut_range_are_rewritten(self):\n        worksheet = Worksheet()\n        worksheet.A1.formula = '=B1'\n        worksheet.A2.formula = '=B2'\n        worksheet.A3.formula = '=B3'\n        worksheet.A4.formula = 'B1'\n        worksheet.A5.formula = '=$B$1'\n\n        rewrite_source_sheet_formulae_for_cut(worksheet, (2, 1, 2, 2), 3, 4)\n\n        self.assertEquals(worksheet.A1.formula, '=C4')\n        self.assertEquals(worksheet.A2.formula, '=C5')\n        self.assertEquals(worksheet.A3.formula, '=B3')\n        self.assertEquals(worksheet.A4.formula, 'B1')\n        self.assertEquals(worksheet.A5.formula, '=$C$4')\n\n\n    def test_source_sheet_cell_ranges_inside_cut_range_are_rewritten(self):\n        worksheet = Worksheet()\n        worksheet.A1.formula = '=B1:B2'\n        worksheet.A2.formula = '=sum(B1:B2)'\n        worksheet.A3.formula = '=B3:B4'\n        worksheet.A4.formula = 'B1:B2'\n        worksheet.A5.formula = '=$B$1:$B$2'\n\n        rewrite_source_sheet_formulae_for_cut(worksheet, (2, 1, 2, 2), 3, 4)\n\n        self.assertEquals(worksheet.A1.formula, '=C4:C5')\n        self.assertEquals(worksheet.A2.formula, '=sum(C4:C5)')\n        self.assertEquals(worksheet.A3.formula, '=B3:B4')\n        self.assertEquals(worksheet.A4.formula, 'B1:B2')\n        self.assertEquals(worksheet.A5.formula, '=$C$4:$C$5')\n\n"
  },
  {
    "path": "dirigible/sheet/tests/test_sheet.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\nfrom mock import Mock, patch, sentinel\nimport re\nfrom textwrap import dedent\n\nfrom django.contrib.auth.models import User\n\nfrom dirigible.test_utils import ResolverDjangoTestCase\n\nfrom sheet.models import copy_sheet_to_user, Sheet\nfrom user.models import OneTimePad\nfrom sheet.worksheet import Worksheet, worksheet_to_json\n\n\n\nclass CopySheetForUserTest(ResolverDjangoTestCase):\n\n    def test_copy_sheet_allows_other_users_to_copy_public_sheets(self):\n        user = User(username='Slartibartfast')\n        user.save()\n        worksheet = Worksheet()\n        worksheet.a1.value = 'some-cell-content'\n        sheet = Sheet()\n        sheet.owner = user\n        sheet.is_public = True\n        sheet.jsonify_worksheet(worksheet)\n        sheet.save()\n        original_sheet_id = sheet.id\n        other_user = User(username='Othello')\n        other_user.save()\n\n        retval = copy_sheet_to_user(sheet, other_user)\n\n        other_user_sheets = Sheet.objects.filter(owner=other_user)\n        self.assertEquals(len(other_user_sheets), 1)\n        copied_sheet = other_user_sheets[0]\n        self.assertFalse(copied_sheet.is_public)\n        copied_worksheet = copied_sheet.unjsonify_worksheet()\n        self.assertEquals(copied_worksheet.a1.value, 'some-cell-content')\n        self.assertEquals(copied_sheet.id, retval.id)\n        self.assertNotEquals(retval.id, original_sheet_id)\n\n\nclass SheetModelTest(ResolverDjangoTestCase):\n\n    def test_creation(self):\n        user = User(username='sheet_creation')\n        sheet = Sheet(owner=user)\n        self.assertEquals(sheet.owner, user)\n        self.assertEquals(sheet.name, 'Untitled')\n        self.assertEquals(sheet.width, 52)\n        self.assertEquals(sheet.height, 1000)\n        self.assertEquals(sheet.timeout_seconds, 55)\n        self.assertEquals(sheet.allow_json_api_access, False)\n        self.assertEquals(sheet.is_public, False)\n        self.assertEquals(sheet.contents_json, worksheet_to_json(Worksheet()))\n        self.assertEquals(sheet.column_widths, {})\n        self.assertEquals(\n            sheet.usercode,\n            dedent(\"\"\"\n                load_constants(worksheet)\n\n                # Put code here if it needs to access constants in the spreadsheet\n                # and to be accessed by the formulae.  Examples: imports,\n                # user-defined functions and classes you want to use in cells.\n\n                evaluate_formulae(worksheet)\n\n                # Put code here if it needs to access the results of the formulae.\n            \"\"\")\n        )\n        self.assertEquals(len(sheet.api_key), 36)\n        self.assertIsNotNone(re.match('[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}', sheet.api_key))\n\n\n    def test_unicode(self):\n        user = User(username='sheet_unicode')\n        user.save()\n        sheet = Sheet(owner=user, name='the new sheet name')\n        sheet.save()\n        self.assertEquals(unicode(sheet), 'Sheet %d: %s' % (sheet.id, sheet.name))\n\n\n    def test_uuid_stays_constant_between_reads(self):\n        sheet = Sheet()\n        user = User(username='sheet_uuid_constant')\n        user.save()\n        sheet.owner = user\n        sheet.save()\n        sheet2 = Sheet.objects.get(pk=sheet.id)\n        self.assertEquals(sheet.api_key, sheet2.api_key)\n\n\n    def test_create_private_key_uses_onetimepad(self):\n        sheet = Sheet()\n        sheet.version = 155\n        user = User(username='HashBrown')\n        user.set_password('glurk')\n        user.save()\n        sheet.owner = user\n        self.assertEquals(len(OneTimePad.objects.all()), 0)\n        self.assertEquals(\n            sheet.create_private_key(),\n            OneTimePad.objects.get(user=sheet.owner).guid)\n        sheet._delete_private_key()\n\n\n    def test_delete_private_key_does(self):\n        self.assertEquals(len(OneTimePad.objects.all()), 0)\n        sheet = Sheet()\n        user = User(username='fred')\n        user.save()\n        sheet.owner = user\n        sheet.create_private_key()\n        self.assertEquals(len(OneTimePad.objects.all()), 1)\n        sheet._delete_private_key()\n        self.assertEquals(len(OneTimePad.objects.all()), 0)\n\n\n    @patch('sheet.sheet.worksheet_from_json')\n    def test_unjsonify_worksheet_should_return_worksheet(self, mock_worksheet_from_json):\n        sheet = Sheet()\n        sheet.contents_json = sentinel.contents_json\n\n        worksheet = sheet.unjsonify_worksheet()\n\n        self.assertEquals(worksheet, mock_worksheet_from_json.return_value)\n        self.assertCalledOnce(mock_worksheet_from_json, sentinel.contents_json)\n\n\n    @patch('sheet.sheet.worksheet_to_json')\n    def test_jsonify_worksheet_should_write_json_to_contents_json_field(self, mock_worksheet_to_json):\n        sheet = Sheet()\n\n        sheet.jsonify_worksheet(sentinel.worksheet)\n\n        self.assertCalledOnce(mock_worksheet_to_json, sentinel.worksheet)\n        self.assertEquals(sheet.contents_json, mock_worksheet_to_json.return_value)\n\n\n    @patch('sheet.sheet.json')\n    def test_roundtrip_column_widths_to_db(self, mock_json):\n        COLUMN_WIDTHS = {'1': 11, '2': 22, '3': 33}\n        mock_json.loads.return_value = COLUMN_WIDTHS\n        mock_json.dumps.return_value = sentinel.json\n        user = User(username='sheet_roundtrip_column_widths')\n        user.save()\n        sheet = Sheet(owner=user)\n        DEFAULT_COLUMN_WIDTHS_JSON = '{}'\n        self.assertEquals(\n            mock_json.loads.call_args,\n            ((DEFAULT_COLUMN_WIDTHS_JSON,), {})\n        )\n        sheet.column_widths = COLUMN_WIDTHS\n\n        sheet.save()\n        self.assertEqual(\n            mock_json.dumps.call_args,\n            ((COLUMN_WIDTHS,), {})\n        )\n        pk = sheet.id\n\n        sheet2 = Sheet.objects.get(pk=pk)\n        self.assertEquals(sheet2.column_widths, COLUMN_WIDTHS)\n\n\n    def test_sheet_name_set_on_save_if_name_is_default(self):\n        user = User(username='sheet_name_default')\n        user.save()\n        sheet = Sheet(owner=user)\n        sheet.save()\n        self.assertEquals(sheet.name, 'Sheet %d' % (sheet.id,))\n\n\n    def test_sheet_name_not_set_on_save_if_name_is_not_default(self):\n        user = User(username='sheet_name_non_default')\n        user.save()\n        sheet = Sheet(owner=user)\n        sheet.name = 'new sheet name'\n        sheet.save()\n        self.assertEquals(sheet.name, 'new sheet name')\n\n\n    def test_last_modified(self):\n        last_modified_field = Sheet._meta.get_field_by_name('last_modified')[0]\n        self.assertEquals(last_modified_field.auto_now, True)\n\n\n    def test_version_default(self):\n        version_field = Sheet._meta.get_field_by_name('version')[0]\n        self.assertEquals(version_field.default, 0)\n\n\n    def test_merge_non_calc_attrs_should_copy_some_attrs(self):\n        s1 = Sheet()\n        s1.name = 's1'\n        s1.column_widths = {'s1': 0}\n        s1.contents_json = sentinel.sheet1\n        s2 = Sheet()\n        s2.name = 's2'\n        s2.column_widths = {'s2': 0}\n        s2.contents_json = sentinel.sheet2\n\n        s1.merge_non_calc_attrs(s2)\n\n        self.assertEquals(s1.name, 's2')\n        self.assertEquals(s1.column_widths, {'s2': 0})\n        self.assertEquals(s1.contents_json, sentinel.sheet1)\n\n\n    @patch('sheet.sheet.calculate_with_timeout')\n    def test_calculate_calls_calculate_with_unjsonified_worksheet_and_saves_recalced_json(\n        self, mock_calculate\n    ):\n        sheet = Sheet()\n        sheet.jsonify_worksheet = Mock()\n        sheet.unjsonify_worksheet = Mock()\n        sheet.usercode = sentinel.usercode\n        sheet.timeout_seconds = sentinel.timeout_seconds\n        sheet.create_private_key = Mock()\n        sheet.otp = Mock()\n\n        sheet.calculate()\n\n        self.assertCalledOnce(\n            mock_calculate,\n            sheet.unjsonify_worksheet.return_value,\n            sheet.usercode,\n            sheet.timeout_seconds,\n            sheet.create_private_key.return_value\n        )\n        self.assertCalledOnce(sheet.jsonify_worksheet, sheet.unjsonify_worksheet.return_value)\n\n\n    @patch('sheet.sheet.calculate_with_timeout')\n    def test_calculate_always_deletes_private_key_in_finally_block(\n        self, mock_calculate\n    ):\n        def raiser(*a, **kw):\n            raise Exception()\n        mock_calculate.side_effect = raiser\n        sheet = Sheet()\n        user = User(username='geoff')\n        user.save()\n        sheet.owner = user\n        sheet._delete_private_key = Mock()\n\n        self.assertRaises(Exception, sheet.calculate)\n\n        self.assertCalledOnce(sheet._delete_private_key)\n"
  },
  {
    "path": "dirigible/sheet/tests/test_ui_jsonifier.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\ntry:\n    import simplejson as json\nexcept ImportError:\n    import json\n\ntry:\n    import unittest2 as unittest\nexcept ImportError:\n    import sys\n    assert sys.version.startswith('2.7')\n    import unittest\n\n\nfrom sheet.ui_jsonifier import sheet_to_ui_json_grid_data, sheet_to_ui_json_meta_data\n\nfrom sheet.cell import Cell\nfrom sheet.models import Sheet\nfrom sheet.worksheet import Worksheet\n\n\nclass TestSheetToUIJsonGridData(unittest.TestCase):\n\n    def test_to_ui_json_grid_zero_size(self):\n        expected = {\n            'bottom': 10000,\n            'left': 0,\n            'right': 10000,\n            'topmost': 0\n        }\n        self.assertEquals(json.loads(sheet_to_ui_json_grid_data(Worksheet(), (0, 0, 10000, 10000))), expected)\n\n\n    def test_to_ui_json_grid_ten_by_five_with_content_and_large_range(self):\n        worksheet = Worksheet()\n        worksheet[2, 1].formula = 'Row 1, col 2 formula'\n        worksheet[2, 1].value = 'Row 1, col 2 value'\n        worksheet[2, 1].formatted_value = 'Row 1, col 2 formatted value'\n        worksheet[10, 1].formula = 'Row 1, col 10 formula'\n        worksheet[10, 1].value = 'Row 1, col 10 value'\n        worksheet[10, 1].formatted_value = 'Row 1, col 10 formatted value'\n        worksheet[1, 5].formula = 'Row 5, col 1 formula'\n        worksheet[1, 5].value = 'Row 5, col 1 value'\n        worksheet[1, 5].formatted_value = 'Row 5, col 1 formatted value'\n        worksheet[10, 5].formula = 'Row 5, col 10 formula'\n        worksheet[10, 5].value = 'Row 5, col 10 value'\n        worksheet[10, 5].formatted_value = 'Row 5, col 10 formatted value'\n        expected_json_contents = {\n            '1' : {\n                '2' : {\n                    \"formula\": 'Row 1, col 2 formula',\n                    \"formatted_value\": \"Row 1, col 2 formatted value\"\n                },\n                '10' : {\n                    \"formula\": 'Row 1, col 10 formula',\n                    \"formatted_value\": \"Row 1, col 10 formatted value\"\n                }\n            },\n            '5' : {\n                '1' : {\n                    \"formula\": 'Row 5, col 1 formula',\n                    \"formatted_value\": \"Row 5, col 1 formatted value\"\n                },\n                '10' : {\n                    \"formula\": 'Row 5, col 10 formula',\n                    \"formatted_value\": \"Row 5, col 10 formatted value\"\n                }\n            },\n            'bottom': 10000,\n            'left': 0,\n            'right': 10000,\n            'topmost': 0\n        }\n        self.assertEquals(\n            json.loads(sheet_to_ui_json_grid_data(worksheet, (0, 0, 10000, 10000))),\n            expected_json_contents\n        )\n\n\n    def test_to_ui_json_grid_ten_by_five_with_content_and_small_range(self):\n        worksheet = Worksheet()\n        worksheet[2, 1].formula = 'Row 1, col 2 formula'\n        worksheet[2, 1].value = 'Row 1, col 2 value'\n        worksheet[2, 1].formatted_value = 'Row 1, col 2 formatted value'\n        worksheet[10, 2].formula = 'Row 2, col 10 formula'\n        worksheet[10, 2].value = 'Row 2, col 10 value'\n        worksheet[10, 2].formatted_value = 'Row 2, col 10 formatted value'\n        worksheet[1, 9].formula = 'Row 9, col 1 formula'\n        worksheet[1, 9].value = 'Row 9, col 1 value'\n        worksheet[1, 9].formatted_value = 'Row 9, col 1 formatted value'\n        worksheet[10, 10].formula = 'Row 10, col 10 formula'\n        worksheet[10, 10].value = 'Row 10, col 10 value'\n        worksheet[10, 10].formatted_value = 'Row 10, col 10 formatted value'\n        left, topmost, right, bottom = 1, 2, 10, 9\n        expected_json_contents = {\n            'left': left,\n            'topmost': topmost,\n            'right': right,\n            'bottom': bottom,\n            '2' : {\n                '10' : {\n                    \"formula\": 'Row 2, col 10 formula',\n                    \"formatted_value\": \"Row 2, col 10 formatted value\"\n                }\n            },\n            '9' : {\n                '1' : {\n                    \"formula\": 'Row 9, col 1 formula',\n                    \"formatted_value\": \"Row 9, col 1 formatted value\"\n                },\n            },\n        }\n        self.assertEquals(\n            json.loads(\n                sheet_to_ui_json_grid_data(\n                    worksheet, (left, topmost, right, bottom)\n                )\n            ),\n            expected_json_contents\n        )\n\n\n    def test_to_ui_json_grid(self):\n        self.maxDiff = None\n        worksheet = Worksheet()\n        left, topmost, right, bottom = 3, 4, 6, 8\n        expected = {\n            'left': left,\n            'topmost': topmost,\n            'right': right,\n            'bottom': bottom,\n        }\n        for col in range(1, 11):\n            for row in range(1, 11):\n                cell_value = '(%d, %d)' % (col, row)\n                worksheet[col, row].formatted_value = cell_value\n                if 3 <= col <= 6  and 4 <= row <= 8:\n                    if str(row) not in expected:\n                        expected[str(row)] = {}\n                    expected[str(row)][str(col)] = {\n                        'formatted_value': cell_value\n                    }\n        actual = json.loads(\n            sheet_to_ui_json_grid_data(worksheet, (left, topmost, right, bottom))\n        )\n        self.assertEquals(actual, expected)\n\n\n    def test_sheet_to_ui_json_grid_data_should_not_contain_undefined_cell_values_or_empty_formatted_values(self):\n        worksheet = Worksheet()\n        worksheet.A1.formula = 'abc'\n\n        expected_json_contents = {\n            '1' : {\n                '1' : { \"formula\": 'abc' },\n            },\n            'bottom': 10,\n            'left': 0,\n            'right': 10,\n            'topmost': 0\n        }\n        self.assertEquals(json.loads(sheet_to_ui_json_grid_data(worksheet, (0, 0, 10, 10))), expected_json_contents)\n\n\n    def test_sheet_to_ui_json_grid_data_should_not_contain_none_cell_formulae(self):\n        worksheet = Worksheet()\n        worksheet.A1.value = 123\n\n        expected_json_contents = {\n            '1' : {\n                '1' : { \"formatted_value\": \"123\" },\n            },\n            'bottom': 10,\n            'left': 0,\n            'right': 10,\n            'topmost': 0\n        }\n        self.assertEquals(json.loads(sheet_to_ui_json_grid_data(worksheet, (0, 0, 10, 10))), expected_json_contents)\n\n\n    def test_sheet_to_ui_json_grid_data_should_not_include_totally_empty_cells(self):\n        worksheet = Worksheet()\n        worksheet.A1 = Cell()\n\n        expected_json_contents = {\n            'bottom': 10,\n            'left': 0,\n            'right': 10,\n            'topmost': 0\n        }\n        self.assertEquals(json.loads(sheet_to_ui_json_grid_data(worksheet, (0, 0, 10, 10))), expected_json_contents)\n\n\n    def test_to_ui_json_grid_includes_cell_errors(self):\n        self.maxDiff = None\n        worksheet = Worksheet()\n        worksheet.A1.formula = 'an int'\n        worksheet.A1.value = 123\n        worksheet.A1.error = 'TestingError1'\n        worksheet.B1.formula = 'an int'\n        worksheet.B1.error = 'TestingError2'\n        worksheet.C1.value = 123\n        worksheet.C1.error = 'TestingError3'\n        worksheet.D1.error = 'TestingError4'\n\n        expected_json_contents = {\n            '1' : {\n                '1' : { \"formula\": 'an int', \"error\": \"TestingError1\" },\n                '2' : { \"formula\": 'an int', \"error\": \"TestingError2\" },\n                '3' : { \"error\": \"TestingError3\" },\n                '4' : { \"error\": \"TestingError4\" },\n            },\n            'bottom': 10,\n            'left': 0,\n            'right': 10,\n            'topmost': 0\n        }\n        self.assertEquals(json.loads(sheet_to_ui_json_grid_data(worksheet, (0, 0, 10, 10))), expected_json_contents)\n\n\nclass TestSheetToUIJsonMetaData(unittest.TestCase):\n\n    def test_to_ui_json_meta_data_zero_size(self):\n        sheet = Sheet(width=0, height=0)\n        expected = dict(width=sheet.width, height=sheet.height, name='Untitled')\n        self.assertEquals(json.loads(sheet_to_ui_json_meta_data(sheet, Worksheet())), expected)\n\n\n    def test_to_ui_json_meta_data_ten_by_five_empty(self):\n        sheet = Sheet(width=10, height=5)\n        expected = dict(width=sheet.width, height=sheet.height, name='Untitled')\n        self.assertEquals(json.loads(sheet_to_ui_json_meta_data(sheet, Worksheet())), expected)\n\n\n    def test_to_ui_json_meta_data_includes_worksheet_console_text(self):\n        sheet = Sheet(width=10, height=5)\n        worksheet = Worksheet()\n        worksheet._console_text = ['error1', 'error2']\n        expected = dict(\n            width=sheet.width,\n            height=sheet.height,\n            name='Untitled',\n            console_text=worksheet._console_text)\n        self.assertEquals(json.loads(sheet_to_ui_json_meta_data(sheet, worksheet)), expected)\n\n\n    def test_to_ui_json_meta_data_includes_columns_widths(self):\n        sheet = Sheet(width=10, height=5)\n        sheet.column_widths = {u'1': 1, u'2': 22, u'3': 333}\n        expected = dict(\n            width=sheet.width,\n            height=sheet.height,\n            name='Untitled',\n            column_widths=sheet.column_widths,\n        )\n        self.assertEquals(json.loads(sheet_to_ui_json_meta_data(sheet, Worksheet())), expected)\n\n\n    def test_to_ui_json_meta_data_includes_usercode_errors(self):\n        sheet = Sheet(width=10, height=5)\n        worksheet = Worksheet()\n        worksheet._usercode_error = {\n            'message' : 'ABC',\n            'line' : 123\n        }\n        expected_json_contents = {\n            'width': sheet.width,\n            'height': sheet.height,\n            'name': sheet.name,\n            'usercode_error' : {\n                'message' : 'ABC',\n                'line' : '123'\n            }\n        }\n        self.assertEquals(json.loads(sheet_to_ui_json_meta_data(sheet, worksheet)), expected_json_contents)\n\n"
  },
  {
    "path": "dirigible/sheet/tests/test_views.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\nfrom cgi import parse_qs\nfrom mock import Mock, patch, sentinel\nimport re\nfrom StringIO import StringIO\nfrom urlparse import urlparse\n\nimport django\nfrom django.conf import settings\nfrom django.contrib.auth.models import AnonymousUser, User\nfrom django.core.urlresolvers import reverse\nfrom django.db import transaction\nfrom django.http import (\n    Http404, HttpRequest, HttpResponse, HttpResponseForbidden,\n    HttpResponseRedirect\n)\nfrom django.shortcuts import render\nfrom django.test.testcases import disable_transaction_methods, restore_transaction_methods, TransactionTestCase\n\nfrom dirigible.test_utils import (\n    assert_security_classes_exist, die, ResolverTestCase\n)\n\nfrom sheet.cell import Cell, undefined\nfrom sheet.forms import ImportCSVForm\nfrom sheet.models import Clipboard, Sheet\nfrom sheet.views import (\n    calculate, clear_cells, clipboard, copy_sheet, export_csv, get_json_grid_data_for_ui,\n    get_json_meta_data_for_ui, import_csv, import_xls, page, set_cell_formula,\n    set_column_widths, set_sheet_name, set_sheet_security_settings,\n    set_sheet_usercode, update_sheet_with_version_check\n)\nfrom sheet.worksheet import Worksheet, worksheet_from_json\nfrom sheet.importer import DirigibleImportError\n\nclass SheetViewTestCase(TransactionTestCase, ResolverTestCase):\n    maxDiff = None\n\n    def assertMockUpdaterCalledOnceWithWorksheet(\n        self, mock_update_sheet_with_version_check, sheet,\n        expected_worksheet\n    ):\n        self.assertEquals(\n            len(mock_update_sheet_with_version_check.call_args_list),\n            1\n        )\n        if mock_update_sheet_with_version_check.call_args_list == []:\n            self.fail('Not called')\n        args, kwargs = mock_update_sheet_with_version_check.call_args_list[0]\n        self.assertEquals(args, (sheet, ))\n        self.assertTrue('contents_json' in kwargs)\n        resulting_worksheet = worksheet_from_json(kwargs['contents_json'])\n        self.assertEquals(resulting_worksheet, expected_worksheet)\n\n\n\ndef set_up_view_test(self):\n    self.user = User(username='sheetviewtestuser')\n    self.user.save()\n    self.sheet = Sheet(owner=self.user)\n    self.sheet.save()\n    self.request = HttpRequest()\n    self.request.user = self.user\n\n\ndef create_view_security_test(\n    class_name, view, get_dict=None, post_dict=None, files_dict=None, extra_view_args=None\n):\n    if extra_view_args is None:\n        extra_view_args = []\n\n    class _ViewSecurityTest(SheetViewTestCase):\n\n        setUp = set_up_view_test\n\n        def test_view_login_required(self):\n            request = HttpRequest()\n            request.user = AnonymousUser()\n            request.META['SERVER_NAME'] = 'servername'\n            request.META['SERVER_PORT'] = '80'\n            actual = view(\n                request, self.user.username, self.sheet.id, *extra_view_args\n            )\n            self.assertTrue(isinstance(actual, HttpResponseRedirect))\n\n            redirect_url = urlparse(actual['Location'])\n            self.assertEquals(redirect_url.path, settings.LOGIN_URL)\n\n\n        def test_view_should_raise_on_bad_sheet(self):\n            self.assertRaises(Http404, view, self.request, self.user.username, 1234, *extra_view_args)\n\n\n        def test_view_should_raise_404_if_param_user_not_sheet_user(self):\n            wrong_user = User(username='validbutnotowner')\n            wrong_user.save()\n            self.assertRaises(Http404, view, self.request, wrong_user.username, self.sheet.id, *extra_view_args)\n\n\n        def test_view_should_raise_if_nonexistent_user_in_params(self):\n            self.assertRaises(Http404, view, self.request, 'baduser', self.sheet.id, *extra_view_args)\n\n\n        def test_view_should_return_403_with_template_if_non_admin_request_user_doesnt_match_sheet_owner(self):\n            sheet_owner = User(username='sheetowner')\n            request = HttpRequest()\n            request.user = sheet_owner\n            response = view(\n                request, self.user.username, self.sheet.id, *extra_view_args)\n            self.assertEquals(type(response), HttpResponseForbidden)\n            expected_content = django.template.loader.render_to_string(\"403.html\")\n            self.assertEquals(response.content, expected_content)\n\n\n        def test_view_should_allow_admin_user(self):\n            admin_user = User(username='validadminuser')\n            admin_user.is_staff = True\n            admin_user.save()\n            if post_dict is not None:\n                self.request.POST = post_dict\n            if get_dict is not None:\n                self.request.GET = get_dict\n            self.request.FILES = files_dict\n            self.request.user = admin_user\n\n            response = view(self.request, self.user.username, self.sheet.id, *extra_view_args)\n\n            if type(response) == HttpResponseRedirect:\n                redirect_url = urlparse(response['Location'])\n                self.assertNotEquals(redirect_url.path, settings.LOGIN_URL)\n            else:\n                self.assertEqual(type(response), HttpResponse)\n\n\n    return type(class_name, (_ViewSecurityTest,), {})\n\n\nclass ImportXLSSecurityTest(SheetViewTestCase):\n    post_args = {\"column\": \"1\", \"row\": \"1\"}, {'file': 'file'}\n    view = import_xls\n    setUp = set_up_view_test\n\n    def test_view_login_required(self):\n        request = HttpRequest()\n        request.user = AnonymousUser()\n        request.META['SERVER_NAME'] = 'servername'\n        request.META['SERVER_PORT'] = '80'\n        actual = import_xls(request, self.user.username)\n\n        self.assertTrue(isinstance(actual, HttpResponseRedirect))\n\n        redirect_url = urlparse(actual['Location'])\n        self.assertEquals(redirect_url.path, settings.LOGIN_URL)\n\n\n    def test_cannot_upload_to_another_users_dashboard(self):\n        other_user = User(username='dont mess with my dashboard')\n        other_user.save()\n\n        response = import_xls(self.request, other_user.username)\n\n        self.assertEquals(type(response), HttpResponseForbidden)\n        expected_content = django.template.loader.render_to_string(\"403.html\")\n        self.assertEquals(response.content, expected_content)\n\n\nclass ImportXLSTest(SheetViewTestCase):\n\n    setUp = set_up_view_test\n\n    @patch('sheet.views.mkstemp')\n    @patch('sheet.views.os')\n    @patch('sheet.views.xlrd')\n    def test_import_xls_creates_sheets_for_non_empty_worksheets_using_tempfiles(\n            self, mock_xlrd, mock_os, mock_mkstemp\n    ):\n        def make_sheet(name, cols, rows):\n            sheet = Mock()\n            sheet.name = name\n            sheet.ncols = cols\n            sheet.nrows = rows\n            mock_cell = Mock()\n            mock_cell.value = 'cell value'\n            sheet.cell.return_value = mock_cell\n            return sheet\n        data = [\n            ('first_imported_sheet', 2, 3),\n            ('2nd imported sheet', 2, 3),\n            ('zero rows', 2, 0),\n            ('zero cols', 0, 3),\n        ]\n        sheets = [make_sheet(*datum) for datum in data]\n\n        mock_workbook = Mock()\n        mock_workbook.sheets.return_value = sheets\n        mock_xlrd.open_workbook.return_value = mock_workbook\n        mock_mkstemp.return_value = (sentinel.handle, sentinel.filename)\n        mock_file = Mock()\n        mock_file.name = 'uploaded file.xls'\n        mock_file.read.return_value = sentinel.contents\n\n        self.request.FILES = {}\n        self.request.FILES['file'] = mock_file\n\n        response = import_xls(self.request, self.user.username)\n\n        self.assertTrue(isinstance(response, HttpResponseRedirect))\n\n        self.assertCalledOnce(mock_os.write, sentinel.handle, sentinel.contents)\n        self.assertCalledOnce(mock_xlrd.open_workbook, sentinel.filename)\n\n        actual_sheets = Sheet.objects.all()\n        actual_sheetnames = [sheet.name for sheet in actual_sheets]\n        self.assertTrue('uploaded file - %s' % (sheets[0].name,) in actual_sheetnames)\n        self.assertTrue('uploaded file - %s' % (sheets[1].name,) in actual_sheetnames)\n        self.assertFalse('uploaded file - %s' % (sheets[2].name,) in actual_sheetnames)\n        self.assertFalse('uploaded file - %s' % (sheets[3].name,) in actual_sheetnames)\n\n        self.assertCalledOnce(mock_os.close, sentinel.handle)\n        self.assertCalledOnce(mock_os.unlink, sentinel.filename)\n\n\n    @patch('sheet.views.mkstemp')\n    @patch('sheet.views.os')\n    def test_import_xls_closes_and_deletes_tempfile(self, mock_os, mock_mkstemp):\n        mock_mkstemp.return_value = (sentinel.handle, sentinel.filename)\n        mock_file = Mock()\n\n        def die(*_):\n            raise Exception('urk')\n        mock_os.write.side_effect = die\n\n        self.request.FILES = {}\n        self.request.FILES['file'] = mock_file\n\n        response = import_xls(self.request, self.user.username)\n\n        self.assertCalledOnce(mock_os.close, sentinel.handle)\n        self.assertCalledOnce(mock_os.unlink, sentinel.filename)\n\n        self.assertTrue(isinstance(response, HttpResponse))\n        self.assertEquals(\n            response.content,\n            render(\n                self.request,\n                'import_xls_error.html',\n                {},\n            ).content\n        )\n\n\n    @patch('sheet.views.xlrd')\n    @patch('sheet.views.os')\n    @patch('sheet.views.worksheet_from_excel')\n    def test_import_xls_imports_values_and_calls_calculate_on_each_sheet(\n        self, mock_worksheet_from_excel, mock_os, mock_xlrd\n    ):\n        mock_xl_sheet1 = Mock()\n        mock_xl_sheet1.name = 'xl sheet 1'\n        mock_xl_sheet1.ncols = 3\n        mock_xl_sheet1.nrows = 4\n        mock_xl_sheet2 = Mock()\n        mock_xl_sheet2.name = 'xl sheet 2'\n        mock_xl_sheet2.ncols = 3\n        mock_xl_sheet2.nrows = 4\n        mock_workbook = Mock()\n        mock_workbook.sheets.return_value = [mock_xl_sheet1, mock_xl_sheet2]\n        mock_xlrd.open_workbook.return_value = mock_workbook\n\n        expected_ws1 = Worksheet()\n        expected_ws1.name = 'ws1'\n        expected_ws1.A1.formula = 'xl sheet 1'\n        expected_ws2 = Worksheet()\n        expected_ws2.name = 'ws2'\n        expected_ws2.A1.formula = 'xl sheet 2'\n        sheets = [expected_ws1, expected_ws2]\n        mock_worksheet_from_excel.side_effect = lambda _: sheets.pop(0)\n\n        self.request.FILES = {}\n        self.request.FILES['file'] = Mock()\n\n        response = import_xls(self.request, self.user.username)\n\n        self.assertEquals(\n            mock_worksheet_from_excel.call_args_list,\n            [\n                ((mock_xl_sheet1,), {}),\n                ((mock_xl_sheet2,), {}),\n            ]\n        )\n\n        actual_sheet1 = Sheet.objects.get(name__icontains=mock_xl_sheet1.name)\n        actual_sheet2 = Sheet.objects.get(name__icontains=mock_xl_sheet2.name)\n\n        actual_ws1 = actual_sheet1.unjsonify_worksheet()\n        actual_ws2 = actual_sheet2.unjsonify_worksheet()\n\n        self.assertEquals( actual_ws1.A1.formula, 'xl sheet 1')\n        self.assertEquals( actual_ws1.A1.formatted_value, 'xl sheet 1')\n        self.assertEquals(\n            actual_ws1.A1.formatted_value,\n            'xl sheet 1',\n            'possibly failed because recalc not done'\n        )\n\n        self.assertEquals( actual_ws2.A1.formula, 'xl sheet 2')\n        self.assertEquals( actual_ws2.A1.formatted_value, 'xl sheet 2')\n        self.assertEquals(\n            actual_ws2.A1.formatted_value,\n            'xl sheet 2',\n            'possibly failed because recalc not done'\n        )\n\n        self.assertTrue(isinstance(response, HttpResponseRedirect))\n        self.assertEquals(response['Location'], '/')\n\n\n    @patch('sheet.views.calculate', die())\n    @patch('sheet.views.xlrd')\n    @patch('sheet.views.os')\n    @patch('sheet.views.worksheet_from_excel')\n    def test_import_xls_reports_success_on_exception_from_calculate(\n        self, mock_worksheet_from_excel, mock_os, mock_xlrd\n    ):\n        mock_xl_sheet1 = Mock()\n        mock_xl_sheet1.name = 'xl sheet 1'\n        mock_xl_sheet1.ncols = 3\n        mock_xl_sheet1.nrows = 4\n        mock_workbook = Mock()\n        mock_workbook.sheets.return_value = [mock_xl_sheet1]\n        mock_xlrd.open_workbook.return_value = mock_workbook\n\n        expected_ws1 = Worksheet()\n        expected_ws1.name = 'ws1'\n        expected_ws1.A1.formula = 'xl sheet 1'\n        sheets = [expected_ws1]\n        mock_worksheet_from_excel.side_effect = lambda _: sheets.pop(0)\n\n        self.request.FILES = {}\n        self.request.FILES['file'] = Mock()\n\n        response = import_xls(self.request, self.user.username)\n\n        self.assertTrue(isinstance(response, HttpResponseRedirect))\n        self.assertEquals(response['Location'], '/')\n\n\n\nTEST_SIMPLE_CSV = '''1,2,3'''\n\nTEST_EVIL_CSV_WITH_BLANKROW_SPACES_AND_DIFFERENT_LENGTH_ROWS = '''\n1,2,3\na, b,c,d\n=10,=20,=30'''\n\nImportCSVSecurityTest = create_view_security_test(\n    \"ImportCSVSecurityTest\", import_csv,\n    post_dict={\"column\": \"1\", \"row\": \"1\"},\n    files_dict={'file': 'file'}\n)\n\nclass ImportCSVTest(SheetViewTestCase):\n\n    setUp = set_up_view_test\n\n\n    @patch('sheet.views.update_sheet_with_version_check')\n    def test_import_csv_should_import_csv_and_update_sheet_with_version_check(\n        self, mock_update_sheet_with_version_check\n    ):\n        worksheet = Worksheet()\n        for column in range(2, 7):\n            for row in range(3, 9):\n                worksheet[(column, row)].formula = 'old'\n        self.sheet.jsonify_worksheet(worksheet)\n        self.sheet.save()\n\n        test_csv_file = StringIO(TEST_EVIL_CSV_WITH_BLANKROW_SPACES_AND_DIFFERENT_LENGTH_ROWS)\n        test_csv_file.name = 'filename'\n        test_csv_file.size = 10\n\n        self.request.FILES['file'] = test_csv_file\n        self.request.POST['column'] = 3\n        self.request.POST['csv_encoding'] = 'excel'\n        self.request.POST['row'] = 4\n\n        mock_update_sheet_with_version_check.return_value = True\n        response = import_csv(self.request, self.user.username, self.sheet.id)\n\n        self.assertTrue(isinstance(response, HttpResponseRedirect))\n        self.assertEquals(response['Location'], '/user/sheetviewtestuser/sheet/%d/' % (self.sheet.id, ))\n\n        expected = [\n            # outside top left corner unchanged\n            ((2, 4), 'old'),\n            ((3, 3), 'old'),\n\n            # outside bottom right corner unchanged\n            ((6, 7), 'old'),\n            ((5, 8), 'old'),\n\n            # inside the imported area\n            ((3, 4), 'old'), #blank row in csv has no effect\n            ((4, 4), 'old'),\n            ((5, 4), 'old'),\n            ((3, 5), '1'),\n            ((4, 5), '2'),\n            ((5, 5), '3'),\n            ((3, 6), 'a'),\n            ((4, 6), ' b'),\n            ((5, 6), 'c'),\n            ((6, 6), 'd'),\n            ((3, 7), '=10'),\n            ((4, 7), '=20'),\n            ((5, 7), '=30'),\n        ]\n        (call_args, call_kwargs) = mock_update_sheet_with_version_check.call_args_list[0]\n        self.assertEquals(call_args, (self.sheet,))\n        self.assertEquals(call_kwargs.keys(), ['contents_json'])\n        resulting_worksheet = worksheet_from_json(call_kwargs['contents_json'])\n        for location, value in expected:\n            self.assertEquals(\n                resulting_worksheet[location].formula, value,\n                'location %s: %s != %s' % (location, resulting_worksheet[location].formula, value)\n            )\n\n\n    @patch('sheet.views.ImportCSVForm')\n    def test_import_csv_handles_null_file(self, mock_import_csv_form):\n        mock_import_csv_form.side_effect = lambda *args, **kwargs: ImportCSVForm(*args, **kwargs)\n\n        self.request.POST['column'] = 3\n        self.request.POST['row'] = 4\n        self.request.POST['csv_encoding'] = 'excel'\n        self.request.FILES = {}\n\n        response = import_csv(self.request, self.user.username, self.sheet.id)\n\n        self.assertEquals(\n            mock_import_csv_form.call_args_list,\n            [((self.request.POST, self.request.FILES), {})]\n        )\n\n        self.assertTrue(isinstance(response, HttpResponse))\n        self.assertTrue('not in a recognised CSV format' in response.content)\n\n\n    @patch('sheet.views.worksheet_from_csv')\n    @patch('sheet.views.get_object_or_404')\n    def test_view_uses_and_handles_errors_from_worksheet_from_csv(\n            self, mock_get_object_or_404, mock_worksheet_from_csv\n    ):\n        def raiser(*args, **kwargs):\n            raise DirigibleImportError('foo')\n        mock_worksheet_from_csv.side_effect = raiser\n\n        self.request.POST['column'] = 3\n        self.request.POST['row'] = 4\n        mock_file = Mock()\n        mock_file.readlines = lambda : []\n        self.request.FILES = {'file': mock_file}\n\n        mock_sheet = Sheet(owner=self.user)\n        mock_sheet.save()\n        mock_worksheet = Mock()\n        mock_sheet.unjsonify_worksheet = lambda : mock_worksheet\n        mock_get_object_or_404.return_value = mock_sheet\n\n        for csv_encoding in ['excel', 'other']:\n            self.request.POST['csv_encoding'] = csv_encoding\n\n            response = import_csv(self.request, self.user.username, self.sheet.id)\n\n            self.assertCalledOnce(mock_worksheet_from_csv,\n                    mock_worksheet, mock_file, 3, 4, csv_encoding=='excel'\n            )\n            self.assertTrue(isinstance(response, HttpResponse))\n            self.assertTrue('not in a recognised CSV format' in response.content)\n            mock_worksheet_from_csv.reset_mock()\n\n\n    @patch('sheet.views.worksheet_from_csv')\n    @patch('sheet.views.calculate')\n    @patch('sheet.views.update_sheet_with_version_check')\n    def test_view_calls_calculate_view_after_update_sheet(\n        self, mock_update_sheet_with_version_check, mock_calculate,\n        mock_worksheet_from_csv\n    ):\n        self.request.POST['column'] = 3\n        self.request.POST['row'] = 4\n        self.request.POST['csv_encoding'] = 'excel'\n        mock_file = Mock()\n        mock_file.readlines = lambda : []\n        self.request.FILES = {'file': mock_file}\n        mock_worksheet_from_csv.return_value = Worksheet()\n        mock_update_sheet_with_version_check.return_value = True\n        import_csv(self.request, self.user.username, self.sheet.id)\n\n        self.assertCalledOnce(\n            mock_calculate,\n            self.request, self.sheet.owner.username, self.sheet.id,\n        )\n\n\n\nExportCSVSecurityTest = create_view_security_test(\n    \"ExportCSVSecurityTest\",\n    export_csv,\n    extra_view_args = ['excel']\n)\n\nclass ExportCSVTest(SheetViewTestCase):\n\n    setUp = set_up_view_test\n\n    @patch('sheet.views.get_object_or_404')\n    @patch('sheet.views.worksheet_to_csv')\n    def test_export_excel_csv_should_produce_csv_with_correct_http_headers_and_content(\n        self, mock_worksheet_to_csv, mock_get_object\n    ):\n        mock_sheet = mock_get_object.return_value\n        mock_sheet.owner = self.user\n        mock_sheet.name = \"Algernon\"\n        expected_filename = \"%s.csv\" % (mock_sheet.name,)\n\n        expected_content = \"Hello world\"\n        mock_worksheet_to_csv.return_value = expected_content\n\n        response = export_csv(self.request, self.user.username, self.sheet.id, 'excel')\n\n        self.assertCalledOnce(\n                mock_worksheet_to_csv,\n                mock_sheet.unjsonify_worksheet.return_value, encoding='windows-1252'\n        )\n\n        self.assertEquals(response.status_code, 200)\n        self.assertEquals(response['Content-Type'], 'text/csv')\n        self.assertEquals(response['Content-Disposition'], 'attachment; filename=%s' % (expected_filename,))\n        self.assertEquals(response['Content-Length'], str(len(expected_content)))\n\n        self.assertEquals(response.content, expected_content)\n\n\n    @patch('sheet.views.get_object_or_404')\n    @patch('sheet.views.worksheet_to_csv')\n    def test_export_unicode_csv_should_produce_csv_with_correct_http_headers_and_content(\n        self, mock_worksheet_to_csv, mock_get_object\n    ):\n        mock_sheet = mock_get_object.return_value\n        mock_sheet.owner = self.user\n        mock_sheet.name = \"Algernon\"\n        expected_filename = \"%s.csv\" % (mock_sheet.name,)\n\n        expected_content = \"Hello world\"\n        mock_worksheet_to_csv.return_value = expected_content\n\n        response = export_csv(self.request, self.user.username, self.sheet.id, 'unicode')\n\n        self.assertCalledOnce(\n                mock_worksheet_to_csv,\n                mock_sheet.unjsonify_worksheet.return_value, encoding='utf-8'\n        )\n\n\n        self.assertEquals(response.status_code, 200)\n        self.assertEquals(response['Content-Type'], 'text/csv')\n        self.assertEquals(response['Content-Disposition'], 'attachment; filename=%s' % (expected_filename,))\n        self.assertEquals(response['Content-Length'], str(len(expected_content)))\n\n        self.assertEquals(response.content, expected_content)\n\n\n    def test_export_excel_csv_handles_encoding_error_and_returns_message(self):\n        some_kanji = u'\\u30bc\\u30ed\\u30a6\\u30a3\\u30f3\\u30b0'\n        worksheet = Worksheet()\n        worksheet.a1.value = some_kanji\n        self.sheet.jsonify_worksheet(worksheet)\n        self.sheet.save()\n\n        response = export_csv(self.request, self.user.username, self.sheet.id, 'excel')\n\n        self.assertEquals(response.status_code, 200)\n\n        self.assertTrue('Could not export' in response.content)\n\n\n    def test_export_csv_allows_other_users_to_view_public_sheets(self):\n        self.sheet.is_public = True\n        worksheet = Worksheet()\n        worksheet.a1.value = 'some-cell-content'\n        self.sheet.jsonify_worksheet(worksheet)\n        self.sheet.save()\n        other_user = User(username='Othello')\n        other_user.save()\n        self.request.user = other_user\n\n        response = export_csv(self.request, self.user.username, self.sheet.id, 'excel')\n\n        self.assertEquals(response.status_code, 200)\n        self.assertTrue('some-cell-content' in response.content)\n\n\n    def test_export_csv_allows_anonymous_user_to_view_public_sheets(self):\n        self.sheet.is_public = True\n        worksheet = Worksheet()\n        worksheet.a1.value = 'some-cell-content'\n        self.sheet.jsonify_worksheet(worksheet)\n        self.sheet.save()\n        self.request.user = AnonymousUser()\n        self.request.META['SERVER_NAME'] = 'servername'\n        self.request.META['SERVER_PORT'] = '80'\n\n        response = export_csv(self.request, self.user.username, self.sheet.id, 'excel')\n\n        self.assertEquals(response.status_code, 200)\n        self.assertTrue('some-cell-content' in response.content)\n\n\n\nCopySheetSecurityTest = create_view_security_test(\n    \"CopySheetSecurityTest\",\n    copy_sheet\n)\n\nclass CopySheetTest(SheetViewTestCase):\n\n    setUp = set_up_view_test\n\n    def test_copy_sheet_allows_other_users_to_copy_public_sheets(self):\n        self.sheet.is_public = True\n        worksheet = Worksheet()\n        worksheet.a1.value = 'some-cell-content'\n        self.sheet.jsonify_worksheet(worksheet)\n        self.sheet.save()\n        other_user = User(username='Othello')\n        other_user.save()\n        self.request.user = other_user\n\n        response = copy_sheet(self.request, self.user.username, self.sheet.id)\n\n        other_user_sheets = Sheet.objects.filter(owner=other_user)\n        self.assertEquals(len(other_user_sheets), 1)\n        copied_sheet = other_user_sheets[0]\n        self.assertFalse(copied_sheet.is_public)\n        copied_worksheet = copied_sheet.unjsonify_worksheet()\n        self.assertEquals(copied_worksheet.a1.value, 'some-cell-content')\n\n        self.assertTrue(isinstance(response, HttpResponseRedirect))\n\n        redirect_url = urlparse(response['Location'])\n        self.assertEquals(\n            redirect_url.path,\n            reverse(\n                'sheet_page',\n                kwargs={\n                    'username': other_user.username,\n                    'sheet_id': copied_sheet.id,\n                }\n            )\n        )\n\n\n    def test_copy_sheet_requires_login_for_anonymous_user(self):\n        self.sheet.is_public = True\n        worksheet = Worksheet()\n        worksheet.a1.value = 'some-cell-content'\n        self.sheet.jsonify_worksheet(worksheet)\n        self.sheet.save()\n        self.request.user = AnonymousUser()\n        self.request.META['SERVER_NAME'] = 'servername'\n        self.request.META['SERVER_PORT'] = '80'\n        self.request.get_full_path = lambda: 'request-path'\n\n        response = copy_sheet(self.request, self.user.username, self.sheet.id)\n\n        self.assertTrue(isinstance(response, HttpResponseRedirect))\n\n        redirect_url = urlparse(response['Location'])\n        self.assertEquals(redirect_url.path, settings.LOGIN_URL)\n        redirect_query_params = parse_qs(redirect_url.query)\n        self.assertEquals(redirect_query_params['next'], ['request-path'])\n\n\n\n\nPageViewSecurityTest = create_view_security_test(\"PageViewSecurityTest\", page)\n\nclass PageViewTest(SheetViewTestCase):\n\n    setUp = set_up_view_test\n\n    def test_page_should_return_response_for_logged_in_owner(self):\n        response = page(self.request, self.user.username, self.sheet.id)\n        self.assertTrue(isinstance(response, HttpResponse))\n        self.assertEquals(response.status_code, 200)\n\n\n    @patch('sheet.views.render')\n    @patch('sheet.views.get_object_or_404')\n    @patch('sheet.views.ImportCSVForm')\n    def test_page_should_render_template_with_correct_stuff_before_setting_userprofile_flag(\n        self, mock_import_csv_form, mock_get_object_or_404, mock_render\n    ):\n        mock_get_object_or_404.return_value = self.sheet\n\n        def check_userprofile_flag_not_set(*args, **kwargs):\n            self.assertEquals(\n                self.user.get_profile().has_seen_sheet_page,\n                False,\n                'user profile has_seen_sheet_page set to True before first render'\n            )\n            return sentinel.response\n        mock_render.side_effect = check_userprofile_flag_not_set\n\n        actual = page(self.request, self.user.username, self.sheet.id)\n\n        self.assertCalledOnce(\n            mock_render,\n            self.request,\n            'sheet_page.html',\n            {\n                'sheet': self.sheet,\n                'user': self.user,\n                'profile': self.user.get_profile(),\n                'import_form': mock_import_csv_form.return_value,\n            }\n        )\n        self.assertEquals(actual, sentinel.response)\n        self.assertTrue(self.user.get_profile().has_seen_sheet_page)\n\n\n    @patch('sheet.views.Context')\n    @patch('sheet.views.get_template')\n    @patch('sheet.views.send_mail')\n    def test_view_should_send_welcome_email_if_new_user(\n            self, mock_send_mail, mock_get_template, mock_Context\n    ):\n        page(self.request, self.user.username, self.sheet.id)\n\n        self.assertCalledOnce(mock_get_template, 'welcome_email.txt')\n        self.assertCalledOnce(mock_Context, dict(user=self.user))\n        self.assertCalledOnce(\n            mock_get_template.return_value.render,\n            mock_Context.return_value\n        )\n        self.assertCalledOnce(\n            mock_send_mail,\n            'Welcome to Dirigible',\n            mock_get_template.return_value.render.return_value,\n            '',\n            [self.user.email],\n            fail_silently=True\n        )\n\n\n    @patch('sheet.views.render')\n    def test_page_allows_other_users_to_view_public_sheets(self, mock_render):\n        mock_render.side_effect = render\n        self.sheet.is_public = True\n        self.sheet.save()\n        other_user = User(username='Othello')\n        other_user.save()\n        self.request.user = other_user\n\n        response = page(self.request, self.user.username, self.sheet.id)\n\n        self.assertEquals(response.status_code, 200)\n        self.assertTrue('id_usercode' in response.content)\n        (request, template_name, context), kwargs = mock_render.call_args\n\n        self.assertTrue(context['sheet'].public_view_mode)\n\n\n    @patch('sheet.views.render')\n    def test_page_allows_anonymous_user_to_view_public_sheets(self, mock_render):\n        mock_render.side_effect = render\n        self.sheet.is_public = True\n        self.sheet.save()\n        self.request.user = AnonymousUser()\n        self.request.META['SERVER_NAME'] = 'servername'\n        self.request.META['SERVER_PORT'] = '80'\n\n        response = page(self.request, self.user.username, self.sheet.id)\n\n        self.assertEquals(response.status_code, 200)\n        self.assertTrue('id_usercode' in response.content)\n        (request, template_name, context), kwargs = mock_render.call_args\n        self.assertTrue(context['sheet'].public_view_mode)\n\n\nSetCellFormulaSecurityTest = create_view_security_test(\n    \"SetCellFormulaSecurityTest\", set_cell_formula,\n    post_dict={\"column\": \"1\", \"row\": \"1\", \"formula\": \"woo\"}\n)\n\nclass SetCellFormulaTest(SheetViewTestCase):\n\n    setUp = set_up_view_test\n\n    @patch('sheet.views.update_sheet_with_version_check')\n    def test_view_should_set_cell_formula_and_update_sheet_with_version_check(\n        self, mock_update_sheet_with_version_check\n    ):\n        original_worksheet = Worksheet()\n        original_worksheet[23, 89].formula = \"old formula\"\n        original_worksheet[23, 90].formula = \"formula that should remain untouched\"\n        self.sheet.jsonify_worksheet(original_worksheet)\n        self.sheet.save()\n\n        expected_column = 23\n        expected_row = 89\n        expected_formula = 'new formula'\n        self.request.POST[\"column\"] = str(expected_column)\n        self.request.POST[\"row\"] = str(expected_row)\n        self.request.POST[\"formula\"] = expected_formula\n        mock_update_sheet_with_version_check.return_value = True\n\n        response = set_cell_formula(self.request, self.user.username, self.sheet.id)\n\n        self.assertTrue(isinstance(response, HttpResponse))\n        self.assertEquals(response.content, \"OK\")\n\n        ((call_args, call_kwargs),) = mock_update_sheet_with_version_check.call_args_list\n        self.assertEquals(call_args, (self.sheet,))\n        self.assertEquals(call_kwargs.keys(), ['contents_json'])\n        resulting_worksheet = worksheet_from_json(call_kwargs['contents_json'])\n        self.assertEquals(resulting_worksheet[23, 89].formula, 'new formula')\n        self.assertEquals(resulting_worksheet[23, 90].formula, \"formula that should remain untouched\")\n\n\n    def test_other_users_cant_scf_even_on_public_worksheets(self):\n        self.sheet.is_public = True\n        self.sheet.save()\n        other_user = User(username='Othello')\n        other_user.save()\n        self.request.user = other_user\n\n        expected_formula = 'new formula'\n        self.request.POST[\"column\"] = '1'\n        self.request.POST[\"row\"] = '1'\n        self.request.POST[\"formula\"] = expected_formula\n\n        response = set_cell_formula(self.request, self.user.username, self.sheet.id)\n        self.assertEquals(response.status_code, 403)\n\n        resulting_sheet = Sheet.objects.get(pk=self.sheet.id)\n        resulting_worksheet = resulting_sheet.unjsonify_worksheet()\n        self.assertEquals(resulting_worksheet.a1.formula, None)\n\n\n\nClearCellsSecurityTest = create_view_security_test(\n    \"ClearCellsSecurityTest\",\n    clear_cells,\n    post_dict = {'range': '2,1,3,3'},\n)\n\n\nclass ClearCellsTest(SheetViewTestCase):\n\n    setUp = set_up_view_test\n\n    @patch('sheet.views.update_sheet_with_version_check')\n    def test_view_should_clear_range_given_and_update_sheet_with_version_check(\n        self, mock_update_sheet_with_version_check\n    ):\n        mock_update_sheet_with_version_check.return_value = True\n\n        worksheet = Worksheet()\n        worksheet.B1.formula = 'be one'\n        worksheet.B2.formula = 'bee to'\n        worksheet.B3.formula = 'be free'\n        self.sheet.jsonify_worksheet(worksheet)\n        self.sheet.save()\n\n        self.request.POST = {'range': '2,1,2,2'}\n\n        clear_cells(self.request, self.user.username, self.sheet.id)\n\n        expected_worksheet = Worksheet()\n        expected_worksheet.B1.formula = None\n        expected_worksheet.B2.formula = None\n        expected_worksheet.B3.formula = 'be free'\n\n        self.assertMockUpdaterCalledOnceWithWorksheet(\n            mock_update_sheet_with_version_check,\n            self.sheet,\n            expected_worksheet\n        )\n\n\n    @patch('sheet.views.update_sheet_with_version_check')\n    def test_view_should_return_ok_if_successful(\n        self, mock_update_sheet_with_version_check\n    ):\n        mock_update_sheet_with_version_check.return_value = True\n\n        self.request.POST = {'range': '2,1,3,2'}\n\n        response = clear_cells(self.request, self.user.username, self.sheet.id)\n\n        self.assertTrue(isinstance(response, HttpResponse))\n        self.assertEquals(response.content, \"OK\")\n\n\n    @patch('sheet.views.update_sheet_with_version_check')\n    def test_view_should_return_fail_if_update_fails(\n        self, mock_update_sheet_with_version_check\n    ):\n        mock_update_sheet_with_version_check.return_value = False\n\n        self.request.POST = {'range': '2,1,3,2'}\n\n        response = clear_cells(self.request, self.user.username, self.sheet.id)\n\n        self.assertTrue(isinstance(response, HttpResponse))\n        self.assertEquals(response.content, \"FAILED\")\n\n\n\nSetSheetUsercodeSecurityTest = create_view_security_test(\n    \"SetSheetUsercodeSecurityTest\", set_sheet_usercode,\n    post_dict={ 'usercode' : '' }\n)\n\nclass SetSheetUsercodeTest(SheetViewTestCase):\n\n    setUp = set_up_view_test\n\n    @patch('sheet.views.update_sheet_with_version_check')\n    def test_view_should_set_sheet_usercode_and_updates_version(self, mock_update_sheet_with_version_check):\n        expected_usercode = 'mary had a leg of lamb'\n        self.request.POST[\"usercode\"] = str(expected_usercode)\n        mock_update_sheet_with_version_check.return_value = True\n\n        response = set_sheet_usercode(self.request, self.user.username, self.sheet.id)\n\n        self.assertTrue(isinstance(response, HttpResponse))\n        self.assertEquals(response.content, \"OK\")\n\n        ((call_args, call_kwargs),) = mock_update_sheet_with_version_check.call_args_list\n        self.assertEquals(call_args, (self.sheet,))\n        self.assertEquals(call_kwargs, {'usercode': expected_usercode})\n\n\n    def test_view_set_sheet_usercode_fixes_windows_line_endings(self):\n        submitted_usercode = 'mary had a\\r\\n leg of\\r lamb'\n        expected_usercode = 'mary had a\\n leg of\\r lamb'\n        self.request.POST[\"usercode\"] = str(submitted_usercode)\n\n        set_sheet_usercode(self.request, self.user.username, self.sheet.id)\n\n        sheet_from_db = Sheet.objects.get(pk=self.sheet.id)\n        self.assertEquals(\n            sheet_from_db.usercode,\n            expected_usercode\n        )\n\n\nSetSheetSecuritySettingsSecurityTest = create_view_security_test(\n    \"SetSheetSecuritySettingsSecurityTest\", set_sheet_security_settings,\n    post_dict={\n        \"is_public\": \"false\",\n        \"api_key\": \"this_is_the_api_key\",\n        \"allow_json_api_access\": \"true\"\n    }\n)\n\nclass SetSheetSecuritySettingsTest(SheetViewTestCase):\n\n    setUp = set_up_view_test\n\n    @patch('sheet.views.get_object_or_404')\n    def test_view_should_set_sheet_security_settings(self, mock_get_object):\n        mock_sheet = mock_get_object.return_value\n        mock_sheet.owner = self.user\n        mock_sheet.api_key = 'old_api_key'\n        mock_sheet.allow_json_api_access = False\n        mock_sheet.is_public = False\n\n        self.request.POST[\"api_key\"] = 'new_api_key'\n        self.request.POST[\"allow_json_api_access\"] = 'true'\n        self.request.POST[\"is_public\"] = 'true'\n\n        def save_sheet():\n            self.assertEquals(mock_sheet.api_key, 'new_api_key',\n                              'sheet api_key not set before save')\n            self.assertEquals(mock_sheet.allow_json_api_access, True,\n                              'sheet json api access not set before save')\n            self.assertEquals(mock_sheet.is_public, True,\n                              'sheet public access not set before save')\n        mock_sheet.save.side_effect = save_sheet\n\n        actual = set_sheet_security_settings(self.request, self.user.username, self.sheet.id)\n\n        self.assertTrue(isinstance(actual, HttpResponse))\n        self.assertEquals(actual.content, 'OK')\n\n        self.assertEquals(\n            mock_get_object.call_args,\n            ((Sheet,), dict(pk=self.sheet.id, owner__username=self.user.username))\n        )\n\n        self.assertEquals(\n            mock_sheet.method_calls,\n            [('save', (), {}), ]\n        )\n\n\n\nSetSheetNameSecurityTest = create_view_security_test(\n    \"SetSheetNameSecurityTest\", set_sheet_name,\n    post_dict={\"new_value\": \"New name\"}\n)\n\nclass SetSheetNameTest(SheetViewTestCase):\n\n    setUp = set_up_view_test\n\n    @patch('sheet.views.get_object_or_404')\n    def test_view_should_set_sheet_name(self, mock_get_object):\n        expected_sheet_name = 'mary had a leg of lamb'\n        self.request.POST[\"new_value\"] = expected_sheet_name\n\n        mock_sheet = mock_get_object.return_value\n        mock_sheet.owner = self.user\n        def save_sheet():\n            self.assertEquals(mock_sheet.name, expected_sheet_name,\n                              'sheet name not set before save')\n        mock_sheet.save.side_effect = save_sheet\n\n        actual = set_sheet_name(self.request, self.user.username, self.sheet.id)\n\n        self.assertTrue(isinstance(actual, HttpResponse))\n        self.assertEquals(actual.content,\n            '{\"is_error\":false, \"html\":\"%s\"}' % (expected_sheet_name,))\n\n        self.assertEquals(\n            mock_get_object.call_args,\n            ((Sheet,), dict(pk=self.sheet.id, owner__username=self.user.username))\n        )\n\n        self.assertEquals(\n            mock_sheet.method_calls,\n            [ ('save', (), {}), ]\n        )\n\n\n    def test_view_should_escape_naughty_characters_in_sheet_name(self):\n        self.request.POST['new_value'] = '<blink>HAI!</blink>'\n        response = set_sheet_name(self.request, self.user.username, self.sheet.id)\n        self.assertEquals(\n            response.content,\n            '{\"is_error\":false, \"html\":\"&lt;blink&gt;HAI!&lt;/blink&gt;\"}')\n\n\nSetColumnWidthsSecurityTest = create_view_security_test(\n    \"SetColumnWidthsSecurityTest\", set_column_widths,\n    post_dict={ \"column_widths\" : '{\"2\":22, \"3\":33}' }\n)\n\n\nclass SetColumnWidthsTest(SheetViewTestCase):\n\n    setUp = set_up_view_test\n\n    @patch('sheet.views.get_object_or_404')\n    def test_view_should_set_column_widths_and_save(self, mock_get_object):\n        self.request.POST[\"column_widths\"] = '{\"2\":22, \"3\":33}'\n\n        sheet = Sheet()\n        sheet.owner = self.user\n        sheet.save = Mock()\n        sheet.column_widths = {u'1':11, u'2': 22222}\n\n        mock_get_object.return_value = sheet\n        response = set_column_widths(\n            self.request, self.user.username, self.sheet.id)\n\n        self.assertTrue(isinstance(response, HttpResponse))\n\n        expected_col_widths = {'1':11, '2':22, '3':33}\n        self.assertEquals(sheet.column_widths, expected_col_widths)\n\n        self.assertEquals(\n            sheet.save.call_args,\n            ((), {})\n        )\n\n\nCalculateSecurityTest = create_view_security_test(\n    \"CalculateTest\", calculate)\n\nclass CalculateTest(SheetViewTestCase):\n\n    setUp = set_up_view_test\n\n\n    @patch('sheet.views.get_object_or_404')\n    @patch('sheet.views.update_sheet_with_version_check')\n    def test_view_should_use_managed_transaction_and_update_sheet_with_version_check(\n        self, mock_update_sheet_with_version_check, mock_get_object_or_404\n    ):\n\n        self.sheet.unjsonify_worksheet = Mock()\n        mock_update_sheet_with_version_check.return_value = True\n\n        worksheet_after_calculate = Worksheet()\n\n        def mock_calculate(*_):\n            worksheet_after_calculate[1, 1].formula = 'calculated'\n            self.sheet.jsonify_worksheet(worksheet_after_calculate)\n        self.sheet.calculate = mock_calculate\n\n        def check_transaction_managed_and_return_patched_sheet(*args, **kwargs):\n            self.assertFalse(transaction.get_autocommit())\n            return self.sheet\n        mock_get_object_or_404.side_effect = check_transaction_managed_and_return_patched_sheet\n\n        response = calculate(self.request, self.user.username, self.sheet.id)\n\n        self.assertTrue(isinstance(response, HttpResponse))\n        self.assertEquals(response.content, 'OK')\n\n        self.assertMockUpdaterCalledOnceWithWorksheet(\n            mock_update_sheet_with_version_check,\n            self.sheet,\n            worksheet_after_calculate\n        )\n\n\n\n    @patch('sheet.views.update_sheet_with_version_check')\n    def test_view_response_if_update_sheet_with_version_check_fails(\n        self, mock_update_sheet_with_version_check\n    ):\n        mock_update_sheet_with_version_check.return_value = False\n\n        actual = calculate(\n            self.request, self.user.username, self.sheet.id\n        )\n\n        self.assertTrue(isinstance(actual, HttpResponse))\n        self.assertEquals(\n            actual.content,\n            '{ \"message\": \"Recalc aborted: sheet changed\" }'\n        )\n\n\n    @patch('sheet.views.get_object_or_404')\n    @patch('sheet.views.Sheet.objects.get')\n    @patch('sheet.views.transaction.commit')\n    @patch('sheet.views.update_sheet_with_version_check')\n    def test_view_merges_any_minor_changes_using_transaction(\n        self, mock_update_sheet_with_version_check, mock_commit, mock_sheet_get, mock_get_object_or_404\n    ):\n        updated_sheet_from_db = Mock()\n\n        self.sheet.calculate = Mock()\n        self.sheet.merge_non_calc_attrs = Mock()\n        mock_get_object_or_404.return_value = self.sheet\n\n        calls = []\n        self.sheet.calculate.side_effect = lambda *_, **__: calls.append(\"sheet.calculate\")\n        mock_commit.side_effect = lambda *_, **__: calls.append(\"commit\")\n\n        def mock_get_side_effect(*_, **__):\n            calls.append(\"Sheet.get\")\n            return updated_sheet_from_db\n        mock_sheet_get.side_effect = mock_get_side_effect\n\n        self.sheet.merge_non_calc_attrs.side_effect = lambda *_, **__: calls.append(\"sheet.merge_non_calc_attrs\")\n        mock_update_sheet_with_version_check.side_effect = lambda *_, **__: calls.append(\"update_sheet_with_version_check\")\n\n        calculate(\n            self.request, self.user.username, self.sheet.id\n        )\n\n        self.assertEquals(\n            calls,\n            [\n                \"sheet.calculate\",\n                \"commit\",\n                \"Sheet.get\",\n                \"sheet.merge_non_calc_attrs\",\n                \"update_sheet_with_version_check\",\n                \"commit\",\n            ]\n        )\n\n        self.assertEquals(\n            self.sheet.merge_non_calc_attrs.call_args_list,\n            [((updated_sheet_from_db,), {})]\n        )\n\n\n    @patch('sheet.views.get_object_or_404')\n    def test_view_rolls_back_and_reraises_if_sheet_calculate_raises_with_uncommitted_changes(\n        self, mock_get_object_or_404\n    ):\n\n        # django.test.TestCase replaces the transaction management\n        # functions with nops, which is generally useful but breaks this\n        # test, as we're checking that the exception we raise isn't masked\n        # by one in the leave_transaction_management saying that there was\n        # a pending commit/rollback when the view returned.\n        restore_transaction_methods()\n\n        try:\n            transaction.commit()\n            mock_get_object_or_404.return_value = self.sheet\n            original_sheet_version = self.sheet.version\n            expected_exception = Exception(\"Expected exception\")\n\n            def mock_calculate(*_):\n                self.sheet.version += 5\n                self.sheet.save()\n                raise expected_exception\n            self.sheet.calculate = mock_calculate\n\n            try:\n                calculate(self.request, self.user.username, self.sheet.id)\n                self.fail(\"No exception raised by calculate!\")\n            except Exception, e:\n                self.assertEquals(e, expected_exception)\n\n            reloaded_sheet = Sheet.objects.get(pk=self.sheet.id)\n            self.assertEquals(reloaded_sheet.version, original_sheet_version)\n\n        finally:\n            # Because we committed the changes to the text fixture at the\n            # start of the try/catch, we need to remove them as otherwise\n            # the next test will break\n            self.sheet.delete()\n            self.user.delete()\n            transaction.commit()\n            disable_transaction_methods()\n\n\n    @patch('sheet.views.get_object_or_404')\n    def test_view_rolls_back_and_reraises_if_get_object_raises_with_uncommitted_changes(\n        self, mock_get_object_or_404\n    ):\n        try:\n            # django.test.TestCase replaces the transaction management\n            # functions with nops, which is generally useful but breaks this\n            # test, as we're checking that the exception we raise isn't masked\n            # by one in the leave_transaction_management saying that there was\n            # a pending commit/rollback when the view returned.\n\n            restore_transaction_methods()\n            transaction.commit()\n\n            expected_exception = Exception(\"Expected exception\")\n            def set_dirty_and_raise_exception(*args, **kwargs):\n                transaction.set_dirty()\n                raise expected_exception\n            mock_get_object_or_404.side_effect = set_dirty_and_raise_exception\n\n            try:\n                calculate(self.request, self.user.username, self.sheet.id)\n                self.fail(\"No exception raised by calculate!\")\n            except Exception, e:\n                self.assertEquals(e, expected_exception)\n\n        finally:\n            # Because we committed the changes to the text fixture at the\n            # start of the try/catch, we need to remove them as otherwise\n            # the next test will break\n            self.sheet.delete()\n            self.user.delete()\n            transaction.commit()\n            disable_transaction_methods()\n\n\nGetJsonGridDataForUISecurityTest = create_view_security_test(\n    \"GetJsonGridDataForUISecurityTest\",\n    get_json_grid_data_for_ui,\n    get_dict={'range': '1,2,3,4'}\n)\n\nclass GetJsonGridDataForUITest(SheetViewTestCase):\n\n    setUp = set_up_view_test\n\n\n    @patch('sheet.views.sheet_to_ui_json_grid_data')\n    @patch('sheet.views.get_object_or_404')\n    def test_returns_json_grid_data_for_range_using_get_object_or_404_if_range_specified(\n        self, mock_get_object, mock_sheet_to_ui_json_grid_data\n    ):\n        mock_sheet = mock_get_object.return_value\n        mock_sheet.owner = self.user\n        mock_sheet_to_ui_json_grid_data.return_value = \"A random string that can live in a HttpResponse's content\"\n        self.request.GET['range'] = '1, 2, 3, 4'\n\n        response = get_json_grid_data_for_ui(self.request, self.user.username, self.sheet.id)\n\n        self.assertTrue(isinstance(response, HttpResponse))\n        self.assertFalse(mock_sheet.calculate.called)\n        self.assertCalledOnce(\n            mock_sheet_to_ui_json_grid_data,\n            mock_sheet.unjsonify_worksheet.return_value, (1, 2, 3, 4)\n        )\n        self.assertEquals(response.content, mock_sheet_to_ui_json_grid_data.return_value)\n\n\n    def test_view_allows_other_users_to_view_public_sheets(self):\n        self.sheet.is_public = True\n        self.sheet.save()\n        other_user = User(username='Othello')\n        other_user.save()\n        self.request.user = other_user\n        self.request.GET['range'] = '1, 2, 3, 4'\n\n        response = get_json_grid_data_for_ui(self.request, self.user.username, self.sheet.id)\n\n        self.assertEquals(response.status_code, 200)\n        self.assertTrue('topmost' in response.content)\n\n\n    def test_view_allows_anonymous_user_to_view_public_sheets(self):\n        self.sheet.is_public = True\n        self.sheet.save()\n        self.request.user = AnonymousUser()\n        self.request.META['SERVER_NAME'] = 'servername'\n        self.request.META['SERVER_PORT'] = '80'\n        self.request.GET['range'] = '1, 2, 3, 4'\n\n        response = get_json_grid_data_for_ui(self.request, self.user.username, self.sheet.id)\n\n        self.assertEquals(response.status_code, 200)\n        self.assertTrue('topmost' in response.content)\n\n\nGetJsonMetaDataForUISecurityTest = create_view_security_test(\n    \"GetJsonMetaDataForUISecurityTest\",\n    get_json_meta_data_for_ui\n)\n\nclass GetJsonMetaDataForUITest(SheetViewTestCase):\n\n    setUp = set_up_view_test\n\n    @patch('sheet.views.sheet_to_ui_json_meta_data')\n    @patch('sheet.views.get_object_or_404')\n    def test_get_json_meta_data_for_ui_should_return_unrecalculated_sheet_to_ui_json_using_get_object_or_404(\n        self, mock_get_object, mock_sheet_to_ui_json_meta_data\n    ):\n        mock_sheet = mock_get_object.return_value\n        mock_sheet.owner = self.user\n        mock_sheet_to_ui_json_meta_data.return_value = \"A random string that can live in a HttpResponse's content\"\n\n        response = get_json_meta_data_for_ui(self.request, self.user.username, self.sheet.id)\n\n        self.assertTrue(isinstance(response, HttpResponse))\n        self.assertFalse(mock_sheet.calculate.called)\n        self.assertCalledOnce(\n            mock_sheet_to_ui_json_meta_data,\n            mock_sheet, mock_sheet.unjsonify_worksheet.return_value\n        )\n        self.assertEquals(response.content, mock_sheet_to_ui_json_meta_data.return_value)\n\n\n    def test_view_allows_other_users_to_view_public_sheets(self):\n        self.sheet.is_public = True\n        self.sheet.name = 'lemonparty.org'\n        self.sheet.save()\n        other_user = User(username='Othello')\n        other_user.save()\n        self.request.user = other_user\n\n        response = get_json_meta_data_for_ui(self.request, self.user.username, self.sheet.id)\n\n        self.assertEquals(response.status_code, 200)\n        self.assertTrue('lemonparty.org' in response.content)\n\n\n    def test_view_allows_anonymous_user_to_view_public_sheets(self):\n        self.sheet.is_public = True\n        self.sheet.name = 'lemonparty.org'\n        self.sheet.save()\n        self.request.user = AnonymousUser()\n        self.request.META['SERVER_NAME'] = 'servername'\n        self.request.META['SERVER_PORT'] = '80'\n\n        response = get_json_meta_data_for_ui(self.request, self.user.username, self.sheet.id)\n\n        self.assertEquals(response.status_code, 200)\n        self.assertTrue('lemonparty.org' in response.content)\n\n\n\nclass VersionUpdatesSecurityTest(SheetViewTestCase):\n    '''\n    dummy test class to appease metasecuritytest\n    '''\n\nclass VersionUpdatesTest(SheetViewTestCase):\n\n    setUp = set_up_view_test\n\n    # This test merely checks that all of the views are in one of two lists\n    # -- views that have to update the version and views that don't.  This is\n    # done to make sure that when we add a new view, we think about whether or\n    # not it should update the version.  If it shouldn't, we need do nothing\n    # but put it into the no_version_update list.  If it should, we need to put\n    # it into the version_update list and *also* write a test to make sure it\n    # updates the version in the right way under the right circumstances; this\n    # test can go in the view's own test class.\n    #\n    # NB we also have to explicitly ignore imports and non-functions, which\n    # is a pain, but it's actually suprisingly hard to recognise an object\n    # as a function, especially because it could be decorated with an object\n    # that implements __call__.\n    def test_all_views_considered_for_version_updates(self):\n\n        version_update_view = [\n            'calculate',\n            'clear_cells',\n            'clipboard',\n            'import_csv',\n            'set_cell_formula',\n            'set_sheet_security_settings',\n            'set_sheet_usercode',\n        ]\n\n        no_version_update_view = [\n            'copy_sheet',\n            'export_csv',\n            'get_json_grid_data_for_ui',\n            'get_json_meta_data_for_ui',\n            'new_sheet',\n            'page',\n            'set_column_widths',\n            'set_sheet_name',\n            'import_xls',\n        ]\n\n        utility_functions = [\n            'copy_sheet_to_user',\n            'fetch_users_sheet',\n            'fetch_users_or_public_sheet',\n            'rollback_on_exception',\n            'update_sheet_with_version_check',\n        ]\n\n        extra_imported_stuff_to_ignore = [\n            'AnonymousUser',\n            'Clipboard',\n            'codecs',\n            'Context',\n            'datetime',\n            'DirigibleImportError',\n            'escape',\n            'get_template',\n            'HttpResponse',\n            'HttpResponseForbidden',\n            'HttpResponseRedirect',\n            'ImportCSVForm',\n            'get_object_or_404',\n            'json',\n            'login_required',\n            'mkstemp',\n            'never_cache',\n            'os',\n            'Q',\n            'render',\n            'render_to_string',\n            'RequestContext',\n            'reverse',\n            'Sheet',\n            'send_mail',\n            'sheet_to_ui_json',\n            'sheet_to_ui_json_grid_data',\n            'sheet_to_ui_json_meta_data',\n            'splitext',\n            'transaction',\n            'worksheet_from_excel',\n            'worksheet_from_csv',\n            'worksheet_to_csv',\n            'wraps',\n            'xlrd',\n        ]\n\n        import sheet.views\n        for view_name in dir(sheet.views):\n            if not re.match(\"__.*__\", view_name):\n                if (view_name not in version_update_view and\n                    view_name not in no_version_update_view and\n                    view_name not in utility_functions and\n                    view_name not in extra_imported_stuff_to_ignore\n                   ):\n                    self.fail(\"%s not considered for version update\" % (view_name,))\n\n\n    def test_update_sheet_with_version_check_should_update_increment_version_and_return_true_if_no_change(self):\n        worksheet = Worksheet()\n        worksheet[1, 1].formula = 'old'\n        self.sheet.jsonify_worksheet(worksheet)\n        self.sheet.version = 1\n        self.sheet.save()\n\n        worksheet[1, 1].formula = 'updated'\n        self.sheet.jsonify_worksheet(worksheet)\n        response = update_sheet_with_version_check(self.sheet, contents_json=self.sheet.contents_json)\n\n        self.assertEquals(response, True)\n        sheet_in_db = Sheet.objects.get(pk=self.sheet.id)\n        self.assertEquals(sheet_in_db.version, 2)\n        self.assertEquals(sheet_in_db.unjsonify_worksheet()[1, 1].formula, 'updated')\n\n\n    def test_update_sheet_with_version_check_can_also_update_usercode(self):\n        self.sheet.usercode = 'old'\n        self.sheet.version = 1\n        self.sheet.save()\n\n        response = update_sheet_with_version_check(self.sheet, usercode='updated')\n\n        self.assertEquals(response, True)\n        sheet_in_db = Sheet.objects.get(pk=self.sheet.id)\n        self.assertEquals(sheet_in_db.version, 2)\n        self.assertEquals(sheet_in_db.usercode, 'updated')\n\n\n    def test_update_sheet_with_version_check_returns_false_and_doesnt_update_if_sheet_changed_in_database(self):\n        worksheet = Worksheet()\n        worksheet[1, 1].formula = 'old'\n        old_sheet = self.sheet\n        old_sheet.jsonify_worksheet(worksheet)\n        old_sheet.version = 1\n        old_sheet.save()\n\n        sheet_updated_elsewhere = Sheet.objects.get(pk=self.sheet.id)\n        update_to_ws = Worksheet()\n        update_to_ws[1, 1].formula = 'smooth and silent, just like always'\n        sheet_updated_elsewhere.jsonify_worksheet(update_to_ws)\n        sheet_updated_elsewhere.version += 1\n        sheet_updated_elsewhere.save()\n\n        worksheet[1, 1].formula = 'update attempt'\n        old_sheet.jsonify_worksheet(worksheet)\n        response = update_sheet_with_version_check(old_sheet, contents_json=old_sheet.contents_json)\n\n        self.assertEquals(response, False)\n        sheet_in_db = Sheet.objects.get(pk=self.sheet.id)\n        self.assertEquals(sheet_in_db.version, 2)\n        self.assertEquals(sheet_in_db.unjsonify_worksheet()[1, 1].formula, 'smooth and silent, just like always')\n\n\n\nClipboardViewSecurityTest = create_view_security_test(\n    \"ClipboardViewSecurityTest\",\n    clipboard,\n    post_dict={'range': '2,1,3,3'},\n    extra_view_args=['copy']\n)\n\n\nclass ClipboardViewTest(SheetViewTestCase):\n\n    setUp = set_up_view_test\n\n    def test_copy_gets_formulas_or_formatted_values_and_populates_clipboard(self):\n        self.maxDiff = None\n\n        worksheet = Worksheet()\n        b1_cell = Cell()\n        b1_cell.formula= '=\"a formula\"'\n        worksheet.B1 = b1_cell\n\n        b2_cell = Cell()\n        b2_cell.formatted_value = 'formatted value only'\n        worksheet.B2 = b2_cell\n\n        b3_cell = Cell()\n        b3_cell.formula = 'a constant'\n        b3_cell.value = 'a value as well'\n        b3_cell.formatted_value = 'another formatted value'\n        worksheet.B3 = b3_cell\n\n        self.sheet.jsonify_worksheet(worksheet)\n        self.sheet.save()\n\n        self.request.POST = {'range': '2,1,3,3'}\n        response = clipboard(self.request, self.user.username, self.sheet.id, 'copy')\n        self.assertEquals(response.content, 'OK')\n\n        users_clipboard = Clipboard.objects.get(owner=self.user)\n\n        # Setup test cells to match expectations\n        b2_cell.formula = b2_cell.formatted_value\n        b3_cell.value = undefined\n        b3_cell.formatted_value = 'another formatted value'\n\n        self.assertEquals(\n            dict(users_clipboard.to_cells((0, 0), (1, 2))),\n            {\n                (0, 0):b1_cell,\n                (0, 1):b2_cell,\n                (0, 2):b3_cell,\n                (1, 0):Cell(),\n                (1, 1):Cell(),\n                (1, 2):Cell(),\n            }\n        )\n\n\n    @patch('sheet.views.update_sheet_with_version_check')\n    def test_paste_offsets_range_and_updates_sheet_with_version_check(\n            self, mock_update_sheet_with_version_check):\n        def update_sheet_with_check(sheet, **kwargs):\n            sheet.save()\n            return True\n        mock_update_sheet_with_version_check.side_effect = update_sheet_with_check\n        self.request.POST = {'range': '3,6,3,6'}\n\n        cb = Clipboard()\n        cb.source_left = 1\n        cb.source_top = 1\n        cb.source_right = 1\n        cb.source_bottom = 2\n        cb.owner = self.user\n        cb.contents_json = '{'\\\n                '\"0,0\":{\"formula\":\"was b1\", \"formatted_value\":\"fvb1\"},' \\\n                '\"0,1\":{\"formula\":\"was b2\", \"formatted_value\":\"fvb2\"}'  \\\n            '}'\n        cb.save()\n\n        response = clipboard(self.request, self.user.username, self.sheet.id, 'paste')\n        self.assertEquals(response.content, 'OK')\n\n        expected_worksheet = Worksheet()\n        expected_worksheet[3, 6].formula = 'was b1'\n        expected_worksheet[3, 6].formatted_value = 'fvb1'\n        expected_worksheet[3, 7].formula = 'was b2'\n        expected_worksheet[3, 7].formatted_value = 'fvb2'\n        self.assertMockUpdaterCalledOnceWithWorksheet(\n            mock_update_sheet_with_version_check,\n            self.sheet,\n            expected_worksheet\n        )\n\n\n    @patch('sheet.views.Clipboard')\n    @patch('sheet.views.update_sheet_with_version_check')\n    def test_failing_paste_returns_failure_message_and_doesnt_save_clipboard(\n            self, mock_update_sheet_with_version_check, mockClipboard):\n        mock_clipboard = Mock()\n        mockClipboard.objects.get_or_create.return_value = mock_clipboard, False\n        mock_clipboard.save = lambda: self.fail('should not save clipboard')\n\n        mock_update_sheet_with_version_check.side_effect = lambda *a, **kw: False\n\n        self.request.POST = {'range': '3,6,3,6'}\n        response = clipboard(self.request, self.user.username, self.sheet.id, 'paste')\n\n        self.assertEquals(\n                response.content,\n                '{ \"message\": \"Paste aborted: sheet changed\" }'\n        )\n\n\n    @patch('sheet.views.update_sheet_with_version_check')\n    @patch('sheet.views.get_object_or_404')\n    def test_cut_populates_and_saves_clipboard_then_removes_cells_and_saves_sheet(\n        self, mock_get_object_or_404, mock_update_sheet_with_version_check\n    ):\n        self.maxDiff = None\n\n        worksheet = Worksheet()\n        b1_cell = Cell()\n        b1_cell.formula= '=\"a formula\"'\n        worksheet.B1 = b1_cell\n\n        b2_cell = Cell()\n        b2_cell.formatted_value = 'formatted value only'\n        worksheet.B2 = b2_cell\n\n        b3_cell = Cell()\n        b3_cell.formula = 'a constant'\n        b3_cell.value = 'a value as well'\n        b3_cell.formatted_value = 'another formatted value'\n        worksheet.B3 = b3_cell\n\n        self.sheet.jsonify_worksheet(worksheet)\n        mock_get_object_or_404.return_value = self.sheet\n\n        self.request.POST = {'range': '2,1,3,4'}\n        response = clipboard(self.request, self.user.username, self.sheet.id, 'cut')\n        self.assertEquals(response.content, 'OK')\n\n        users_clipboard = Clipboard.objects.get(owner=self.user)\n\n        # Setup test cells to match expectations\n        b2_cell.formula = b2_cell.formatted_value\n        b3_cell.value = undefined\n        b3_cell.formatted_value = 'another formatted value'\n\n        self.assertEquals(\n            dict(users_clipboard.to_cells((0, 0), (1, 3))),\n            {\n                (0, 0):b1_cell,\n                (0, 1):b2_cell,\n                (0, 2):b3_cell,\n                (0, 3):Cell(),\n                (1, 0):Cell(),\n                (1, 1):Cell(),\n                (1, 2):Cell(),\n                (1, 3):Cell(),\n            }\n        )\n\n        resulting_worksheet = self.sheet.unjsonify_worksheet()\n        self.assertEquals(resulting_worksheet.B1, Cell())\n        self.assertEquals(resulting_worksheet.B2, Cell())\n        self.assertEquals(resulting_worksheet.B3, Cell())\n\n        self.assertEquals(users_clipboard.is_cut, True)\n        self.assertEquals(users_clipboard.source_left, 2)\n        self.assertEquals(users_clipboard.source_top, 1)\n        self.assertEquals(users_clipboard.source_right, 3)\n        self.assertEquals(users_clipboard.source_bottom, 4)\n        self.assertEquals(users_clipboard.source_sheet, self.sheet)\n\n        self.assertCalledOnce(\n            mock_update_sheet_with_version_check,\n            self.sheet, contents_json=self.sheet.contents_json\n        )\n\n\n    @patch('sheet.views.Clipboard')\n    @patch('sheet.views.update_sheet_with_version_check')\n    def test_failing_cut_returns_failure_message_and_doesnt_save_clipboard(\n            self, mock_update_sheet_with_version_check, mockClipboard):\n        mock_clipboard = Mock()\n        mockClipboard.objects.get_or_create.return_value = mock_clipboard, False\n        mock_clipboard.save = lambda: self.fail('should not save clipboard')\n\n        mock_update_sheet_with_version_check.side_effect = lambda *a, **kw: False\n\n        self.request.POST = {'range': '1,2,3,6'}\n        response = clipboard(self.request, self.user.username, self.sheet.id, 'cut')\n\n        self.assertEquals(\n                response.content,\n                '{ \"message\": \"Cut aborted: sheet changed\" }'\n        )\n\n\n    @patch('sheet.views.Clipboard')\n    def test_copy_then_paste_both_save_clipboard_appropriately(self, mockClipboard):\n        mock_clipboard = Mock()\n        mockClipboard.objects.get_or_create.return_value = mock_clipboard, False\n\n        self.request.POST = {'range': '2,1,3,4'}\n        clipboard(self.request, self.user.username, self.sheet.id, 'copy')\n        self.assertCalledOnce(mock_clipboard.copy, self.sheet, (2, 1), (3, 4))\n        self.assertCalledOnce(mock_clipboard.save)\n\n        mock_clipboard.reset_mock()\n        self.request.POST = {'range': '4,5,4,5'}\n        clipboard(self.request, self.user.username, self.sheet.id, 'paste')\n        self.assertCalledOnce(mock_clipboard.paste_to, self.sheet, (4, 5), (4, 5))\n        self.assertCalledOnce(mock_clipboard.save)\n\n\n    @patch('sheet.views.Clipboard')\n    def test_cut_then_paste_to_same_sheet(self, mockClipboard):\n        mock_clipboard = Mock()\n        mockClipboard.objects.get_or_create.return_value = mock_clipboard, False\n\n        self.request.POST = {'range': '2,1,3,4'}\n        clipboard(self.request, self.user.username, self.sheet.id, 'cut')\n        self.assertCalledOnce(mock_clipboard.cut, self.sheet, (2, 1), (3, 4))\n        self.assertCalledOnce(mock_clipboard.save)\n\n        mock_clipboard.reset_mock()\n\n        self.request.POST = {'range': '4,5,4,5'}\n        clipboard(self.request, self.user.username, self.sheet.id, 'paste')\n        self.assertCalledOnce(mock_clipboard.paste_to, self.sheet, (4, 5), (4, 5))\n        self.assertCalledOnce(mock_clipboard.save)\n\n\n\nclass MetaSecurityTest(SheetViewTestCase):\n    def test_security_classes_exist(self):\n        assert_security_classes_exist(self, __name__, excludes=['SheetViewTestCase'])\n\n"
  },
  {
    "path": "dirigible/sheet/tests/test_views_api_0_1.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\nimport json\nfrom datetime import datetime, timedelta\nfrom mock import Mock, patch\nimport unittest\n\nimport django\nfrom django.contrib.auth.models import User\nfrom django.db import transaction\nfrom django.http import Http404, HttpResponse, HttpResponseForbidden\nfrom django.test.testcases import disable_transaction_methods, restore_transaction_methods\n\nfrom sheet.models import Sheet\nfrom sheet.worksheet import Worksheet\nfrom sheet.tests.test_views import set_up_view_test\nfrom dirigible.test_utils import ResolverTestMixins\nfrom user.models import OneTimePad\n\nfrom sheet.views_api_0_1 import calculate_and_get_json_for_api, _sheet_to_value_only_json\n\n\npk_name = 'dirigible_l337_private_key'\n\n\n\nclass CalculateAndGetJsonForApiViewTest(\n    django.test.TransactionTestCase, ResolverTestMixins\n):\n\n    setUp = set_up_view_test\n\n    def tearDown(self):\n        User.objects.all().delete()\n\n\n    def test_should_return_404_if_sheet_owner_does_not_match_username_from_url(self):\n        #standard security test\n        self.sheet.allow_json_api_access = False\n        self.sheet.save()\n        self.request.user = User()\n        self.assertRaises(Http404, lambda: calculate_and_get_json_for_api(self.request, 'some_random_user', self.sheet.id))\n\n\n    def test_returns_403_if_neither_private_nor_api_keys_provided(self):\n        actual = calculate_and_get_json_for_api(self.request, self.sheet.owner.username, self.sheet.id)\n        self.assertTrue(isinstance(actual, HttpResponseForbidden))\n\n\n    def test_returns_403_if_incorrect_private_key_provided(self):\n        self.request.method = 'POST'\n        self.request.POST = {pk_name: 'an incorrect key'}\n        actual = calculate_and_get_json_for_api(self.request, self.sheet.owner.username, self.sheet.id)\n        self.assertTrue(isinstance(actual, HttpResponseForbidden))\n\n\n    def test_works_if_correct_private_key_provided(self):\n        self.request.method = 'POST'\n        self.request.POST = {pk_name: self.sheet.create_private_key()}\n        actual = calculate_and_get_json_for_api(self.request, self.sheet.owner.username, self.sheet.id)\n        self.assertTrue(json.loads(actual.content), {'name': self.sheet.name})\n\n\n    @patch('sheet.views_api_0_1.OneTimePad')\n    def test_erm_checks_private_key_using_correct_filter(\n        self, mock_Otp):\n\n        mock_Otp.objects.filter.return_value = []\n        guid = self.sheet.create_private_key()\n        self.request.method = 'POST'\n        self.request.POST = {pk_name: guid}\n\n        actual = calculate_and_get_json_for_api(\n            self.request,\n            self.sheet.owner.username,\n            self.sheet.id)\n\n        self.assertCalledOnce(\n            mock_Otp.objects.filter,\n            user=self.sheet.owner, guid=guid)\n\n\n    def test_ignores_old_private_key_things(self):\n        otp = OneTimePad(user=self.user)\n        otp.save()\n        otp.creation_time = datetime.today() - timedelta(36000)\n        otp.save()\n        guid = otp.guid\n        self.request.method = 'POST'\n        self.request.POST = {pk_name: guid}\n\n        actual = calculate_and_get_json_for_api(\n            self.request,\n            self.sheet.owner.username,\n            self.sheet.id\n        )\n\n        self.assertTrue(isinstance(actual, HttpResponseForbidden))\n\n\n\n    def test_403s_if_no_private_key_and_api_access_not_allowed_even_if_correct_api_key_provided(self):\n        self.sheet.allow_json_api_access = False\n        self.sheet.api_key = 'correct key'\n        self.sheet.save()\n        self.request.method = 'POST'\n        self.request.POST = {'api_key': 'correct key'}\n        actual = calculate_and_get_json_for_api(self.request, self.sheet.owner.username, self.sheet.id)\n        self.assertTrue(isinstance(actual, HttpResponseForbidden))\n\n\n    def test_403s_if_api_access_allowed_but_incorrect_api_key_provided(self):\n        self.sheet.allow_json_api_access = True\n        self.sheet.save()\n        self.request.method = 'POST'\n        self.request.POST = {'api_key': 'an incorrect key'}\n        actual = calculate_and_get_json_for_api(self.request, self.sheet.owner.username, self.sheet.id)\n        self.assertTrue(isinstance(actual, HttpResponseForbidden))\n\n\n    def test_works_if_correct_api_key_provided_and_access_allowed(self):\n        self.sheet.allow_json_api_access = True\n        self.sheet.api_key = 'sekrit'\n        self.sheet.save()\n        self.request.method = 'POST'\n        self.request.POST = {'api_key': 'sekrit'}\n        actual = calculate_and_get_json_for_api(self.request, self.sheet.owner.username, self.sheet.id)\n        self.assertTrue(json.loads(actual.content), {'name': self.sheet.name})\n\n\n    @patch('sheet.views_api_0_1.transaction')\n    @patch('sheet.views_api_0_1.get_object_or_404')\n    def test_should_call_sheet_calculate_with_transaction(\n        self, mock_get_object, mock_transaction\n    ):\n        mock_sheet = mock_get_object.return_value\n        mock_sheet.owner = self.user\n        mock_sheet.allow_json_api_access = True\n        transaction = Mock()\n        def wrapper(view):\n            def inner(*_, **__):\n                view(*_, **__)\n            return inner\n        mock_transaction.commit_manually = wrapper\n\n        self.request.method = 'POST'\n        self.request.POST['api_key'] = mock_sheet.api_key = 'key'\n\n        mock_sheet.unjsonify_worksheet.return_value = Worksheet()\n\n        response = calculate_and_get_json_for_api(self.request, self.user.username, self.sheet.id)\n\n        self.assertEquals(\n            mock_sheet.calculate.call_args_list,\n            [((), {})]\n        )\n        self.assertCalledOnce(mock_transaction.commit)\n\n\n    @patch('sheet.views_api_0_1.get_object_or_404')\n    def test_rolls_back_and_reraises_if_get_object_raises_with_uncommitted_changes(\n        self, mock_get_object_or_404\n    ):\n        try:\n            # django.test.TestCase replaces the transaction management functions with nops,\n            # which is generally useful but breaks this test, as we're checking that the\n            # exception we raise isn't masked by one in the leave_transaction_management\n            # saying that there was a pending commit/rollback when the view returned.\n            restore_transaction_methods()\n            transaction.commit()\n\n            expected_exception = Exception(\"Expected exception\")\n            def set_dirty_and_raise_exception(*args, **kwargs):\n                transaction.set_dirty()\n                raise expected_exception\n            mock_get_object_or_404.side_effect = set_dirty_and_raise_exception\n\n            try:\n                calculate_and_get_json_for_api(self.request, self.user.username, self.sheet.id)\n            except Exception, e:\n                self.assertEquals(e, expected_exception)\n            else:\n                self.fail(\"No exception raised by calculate_and_get_json_for_api!\")\n\n        finally:\n            # Because we committed the changes to the text fixture at the\n            # start of the try/catch, we need to remove them as otherwise\n            # the next test will break\n            self.sheet.delete()\n            self.user.delete()\n            transaction.commit()\n            disable_transaction_methods()\n\n\n    @patch('sheet.views_api_0_1.get_object_or_404')\n    def test_adds_access_control_header(\n        self, mock_get_object\n    ):\n        calculation_result = Worksheet()\n        calculation_result[1, 3].formula = '=string'\n        calculation_result[1, 3].value = 'test value'\n        calculation_result[2, 5].formula = '=int'\n        calculation_result[2, 5].value = 6\n\n        mock_sheet = mock_get_object.return_value\n        mock_sheet.owner = self.user\n        mock_sheet.unjsonify_worksheet.side_effect = lambda: calculation_result\n        mock_sheet.name = 'mock sheet'\n        mock_sheet.allow_json_api_access = True\n        self.request.method = 'POST'\n        self.request.POST['api_key'] = mock_sheet.api_key = 'key'\n\n        actual = calculate_and_get_json_for_api(self.request, self.user.username, self.sheet.id)\n\n        self.assertEquals(actual['Access-Control-Allow-Origin'], '*')\n\n\n    @patch('sheet.views_api_0_1.transaction')\n    @patch('sheet.views_api_0_1.get_object_or_404')\n    def test_commits_transaction_even_on_sheet_calculate_exception(\n        self, mock_get_object, mock_transaction\n    ):\n        mock_sheet = mock_get_object.return_value\n        mock_sheet.calculate = self.die\n        mock_sheet.owner = self.user\n        mock_sheet.allow_json_api_access = True\n        self.request.method = 'POST'\n        self.request.POST['api_key'] = mock_sheet.api_key = 'key'\n\n        actual = calculate_and_get_json_for_api(\n            self.request, self.user.username, self.sheet.id)\n\n        self.assertCalledOnce(mock_transaction.commit)\n        self.assertTrue(isinstance(actual, HttpResponse))\n        self.assertEquals(actual.content, 'should not be called')\n\n\n    @patch('sheet.views_api_0_1.get_object_or_404')\n    def test_should_return_errors_and_no_values_if_unjsonify_worksheet_result_has_errors(self, mock_get_object):\n        mock_sheet = mock_get_object.return_value\n        mock_sheet.owner = self.user\n\n        worksheet = Worksheet()\n        worksheet[1, 3].formula = '=string'\n        worksheet[1, 3].value = 'test value'\n        worksheet._usercode_error = {\n            \"message\": \"I am an error message\",\n            \"line\": 2\n        }\n        mock_sheet.unjsonify_worksheet.side_effect = lambda: worksheet\n        mock_sheet.allow_json_api_access = True\n        self.request.method = 'POST'\n        self.request.POST['api_key'] = mock_sheet.api_key = 'key'\n\n        expected_json = {\n            \"usercode_error\" : {\n                \"message\": \"I am an error message\",\n                \"line\": \"2\"\n            }\n        }\n\n        actual = calculate_and_get_json_for_api(self.request, self.user.username, self.sheet.id)\n\n        self.assertFalse(mock_sheet.save.called)\n        self.assertTrue(isinstance(actual, HttpResponse))\n        self.assertEquals(actual.content, json.dumps(expected_json))\n\n\n    def die(*_):\n        raise AssertionError('should not be called')\n\n\n    @patch('sheet.views_api_0_1.get_object_or_404')\n    def test_should_handle_cell_formula_overrides_from_POST(self, mock_get_object):\n        mock_sheet = mock_get_object.return_value = Sheet()\n        mock_sheet.owner = self.user\n\n        worksheet = Worksheet()\n        worksheet[1, 1].formula = 'initial-formula1'\n        worksheet[2, 1].formula = '222'\n        worksheet[3, 1].formula = '=B1+111'\n        mock_sheet.jsonify_worksheet(worksheet)\n        mock_sheet.name = 'mysheet'\n        mock_sheet.allow_json_api_access = True\n\n        mock_sheet.save = self.die\n\n        self.request.method = 'POST'\n        self.request.POST = {\n            u'1,1':u'overriddenformula',\n            u'D1':u'=B1+222',\n        }\n        self.request.POST['api_key'] = mock_sheet.api_key = 'key'\n\n        response = calculate_and_get_json_for_api(self.request, self.user.username, self.sheet.id)\n\n        self.assertTrue(isinstance(response, HttpResponse))\n        expected_json = {\n            'name': 'mysheet',\n            '1' : {\n                '1': 'overriddenformula',\n            },\n            '2': {\n                '1': 222,\n            },\n            '3': {\n                '1': 333,\n            },\n            '4': {\n                '1': 444,\n            },\n        }\n        self.assertEquals(json.loads(response.content), expected_json)\n\n\n\n    @patch('sheet.views_api_0_1.get_object_or_404')\n    def test_should_handle_cell_formula_overrides_from_GET(self, mock_get_object):\n        mock_sheet = mock_get_object.return_value = Sheet()\n        mock_sheet.owner = self.user\n\n        worksheet = Worksheet()\n        worksheet[1, 1].formula = 'initial-formula1'\n        worksheet[2, 1].formula = '222'\n        worksheet[3, 1].formula = '=B1+111'\n        mock_sheet.jsonify_worksheet(worksheet)\n        mock_sheet.name = 'mysheet'\n        mock_sheet.allow_json_api_access = True\n        mock_sheet.api_key = 'key'\n\n        mock_sheet.save = self.die\n\n        self.request.GET = {\n            u'1,1':u'overriddenformula',\n            u'D1':u'=B1+222',\n            'api_key': 'key'\n        }\n        self.request.method = 'GET'\n\n        response = calculate_and_get_json_for_api(self.request, self.user.username, self.sheet.id)\n\n        self.assertTrue(isinstance(response, HttpResponse))\n        expected_json = {\n            'name': 'mysheet',\n            '1' : {\n                '1': 'overriddenformula',\n            },\n            '2': {\n                '1': 222,\n            },\n            '3': {\n                '1': 333,\n            },\n            '4': {\n                '1': 444,\n            },\n        }\n        self.assertEquals(json.loads(response.content), expected_json)\n\n\n\nclass TestSheetToValueOnlyJson(unittest.TestCase):\n\n    def test_sheet_to_value_only_json_for_empty_worksheet(self):\n        expected = dict(name='A sheet name')\n        result = _sheet_to_value_only_json(\"A sheet name\", Worksheet())\n        self.assertEquals(json.loads(result), expected)\n\n\n    def test_sheet_to_value_only_json_with_content(self):\n        worksheet = Worksheet()\n        worksheet[2, 1].formula = 'Row 1, col 2 formula'\n        worksheet[2, 1].value = 'Row 1, col 2 value'\n        worksheet[10, 1].formula = 'Row 1, col 10 formula'\n        worksheet[10, 1].value = 'Row 1, col 10 value'\n        worksheet[1, 5].formula = 'Row 5, col 1 formula'\n        worksheet[1, 5].value = 'Row 5, col 1 value'\n        worksheet[10, 5].formula = u'Row 5, col 10 formula avec un \\xe9'\n        worksheet[10, 5].value = u'Row 5, col 10 value avec un autre \\xe9'\n\n        expected_json_contents = {\n            'name': \"Sheet name\",\n            '1': {\n                '5': \"Row 5, col 1 value\",\n            },\n            '2': {\n                '1': \"Row 1, col 2 value\",\n            },\n            '10': {\n                '1': \"Row 1, col 10 value\",\n                '5': u\"Row 5, col 10 value avec un autre \\xe9\",\n            }\n        }\n        result = _sheet_to_value_only_json(\"Sheet name\", worksheet)\n        self.assertEquals(json.loads(result), expected_json_contents)\n\n\n    def test_sheet_to_value_only_json_does_not_include_errors(self):\n        self.maxDiff = None\n\n        worksheet = Worksheet()\n        worksheet.A1.formula = 'a broken formula'\n        worksheet.A1.error = 'TestingError'\n        worksheet.A2.formula = 'an OK formula'\n        worksheet.A2.value = \"23\"\n        expected_json_contents = {\n            'name': \"Sheet name\",\n            '1' : {\n                '2' : '23',\n            }\n        }\n        result = _sheet_to_value_only_json(\"Sheet name\", worksheet)\n        self.assertEquals(json.loads(result), expected_json_contents)\n\n\n    def test_sheet_to_value_only_json_non_string_cell_values(self):\n        known_object = object()\n        worksheet = Worksheet()\n        worksheet.A1.formula = 'an int'\n        worksheet.A1.value = 123\n        worksheet.A2.formula = 'a float'\n        worksheet.A2.value = 1.25\n        worksheet.A3.formula = 'a list'\n        worksheet.A3.value = [1, 2, 3]\n        worksheet.A4.formula = 'an object'\n        worksheet.A4.value = known_object\n        expected_ko = unicode(known_object)\n        expected_json_contents = {\n            'name': \"Sheet name\",\n            '1' : {\n                '1' : 123,\n                '2' : 1.25,\n                '3' : [1, 2, 3],\n                '4' : expected_ko,\n            },\n        }\n        result = _sheet_to_value_only_json(\"Sheet name\", worksheet)\n        self.assertEquals(json.loads(result), expected_json_contents)\n\n"
  },
  {
    "path": "dirigible/sheet/tests/test_worksheet.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\nfrom __future__ import with_statement\n\ntry:\n    import unittest2 as unittest\nexcept ImportError:\n    import unittest\n\nimport codecs\nimport json\nimport simplejson as json\nfrom StringIO import StringIO\nfrom mock import Mock, patch\nfrom threading import Thread\n\nfrom dirigible.test_utils import ResolverTestCase\n\nfrom sheet.cell import Cell, undefined\n\nfrom sheet.worksheet import (\n    Bounds, InvalidKeyError, Worksheet, worksheet_to_csv,\n    worksheet_to_json, worksheet_from_json,\n)\n\n\nclass WorksheetToCsvTest(ResolverTestCase):\n\n    @patch('sheet.worksheet.csv')\n    @patch('sheet.worksheet.StringIO')\n    def test_should_use_stringio_and_return_result(self, mock_stringio_class, mock_csv):\n        mock_stringio_object = mock_stringio_class.return_value\n\n        def check_getvalue_has_been_called():\n            self.assertCalledOnce(mock_stringio_object.getvalue)\n        mock_stringio_object.close.side_effect = check_getvalue_has_been_called\n\n        ws = Worksheet()\n        ws.A1.value = \"test data\"\n        result = worksheet_to_csv(ws, encoding='ascii')\n\n        self.assertCalledOnce(mock_csv.writer, mock_stringio_object)\n        self.assertEquals(result, mock_stringio_object.getvalue.return_value)\n        self.assertCalledOnce(mock_stringio_object.close)\n\n\n    @patch('sheet.worksheet.csv')\n    def test_should_handle_empty_worksheet(self, mock_csv):\n        ws = Worksheet()\n\n        result = worksheet_to_csv(ws, encoding='ascii')\n\n        mock_writer = mock_csv.writer.return_value\n        self.assertFalse(mock_writer.writerow.called)\n\n\n    def test_should_convert_unicode_to_windows_1252(self):\n        ws = Worksheet()\n        ws.a1.value = u'Sacr\\xe9 bleu!'\n        ws.a2.value = u'\\xa312.95'\n        ws.a3.value = u'\\u20ac9.99'\n        result = worksheet_to_csv(ws, encoding='windows-1252')\n        self.assertEquals(\n                result.split('\\r\\n')[:-1],\n                [\n                    ws.a1.value.encode('windows-1252'),\n                    ws.a2.value.encode('windows-1252'),\n                    ws.a3.value.encode('windows-1252'),\n                ]\n        )\n\n\n    def test_raises_on_attempting_to_encode_nonwestern_chars_to_excel_format(self):\n        some_kanji = u'\\u30bc\\u30ed\\u30a6\\u30a3\\u30f3\\u30b0'\n        ws = Worksheet()\n        ws.a1.value = some_kanji\n        self.assertRaises(\n            UnicodeEncodeError,\n            lambda : worksheet_to_csv(ws, encoding='windows-1252')\n        )\n\n\n    def test_handles_cell_values_set_to_non_ascii_bytes(self):\n        a_large_number = 25700000000.0\n        ws = Worksheet()\n        ws.a1.value = a_large_number\n        result = worksheet_to_csv(ws, encoding='windows-1252')\n        stream = StringIO()\n        stream.write(result)\n        stream.seek(0)\n        decoder = codecs.getreader('windows-1252')(stream)\n        decoder.read()\n\n\n    def test_can_convert_unicode_to_utf8(self):\n        some_kanji = u'\\u30bc\\u30ed\\u30a6\\u30a3\\u30f3\\u30b0'\n        ws = Worksheet()\n        ws.a1.value = some_kanji\n        result = worksheet_to_csv(ws, encoding='utf-8')\n        self.assertEquals(\n                result.split('\\r\\n')[:-1],\n                [\n                    ws.a1.value.encode('utf-8'),\n                ]\n        )\n\n\n    @patch('sheet.worksheet.csv')\n    def test_should_process_contents_in_raster_order(self, mock_csv):\n        ws = Worksheet()\n        ws.A1.value = 1\n        ws.B1.value = 2\n        ws.C1.value = 3\n        ws.A2.value = 4\n        ws.B2.value = 5\n        ws.C2.value = 6\n\n        result = worksheet_to_csv(ws, encoding='windows-1252')\n\n        mock_writer = mock_csv.writer.return_value\n        self.assertEquals(\n            mock_writer.writerow.call_args_list,\n            [\n                (([1, 2, 3],), {}),\n                (([4, 5, 6],), {})\n            ]\n        )\n\n\n    @patch('sheet.worksheet.csv')\n    def test_should_include_everything_from_A1_outwards(self, mock_csv):\n        ws = Worksheet()\n        ws.B3.value = 5\n\n        result = worksheet_to_csv(ws, encoding='windows-1252')\n\n        mock_writer = mock_csv.writer.return_value\n        self.assertEquals(\n            mock_writer.writerow.call_args_list,\n            [\n                (([\"\", \"\"],), {}),\n                (([\"\", \"\"],), {}),\n                (([\"\", 5],), {})\n            ]\n        )\n\n\n\nclass WorksheetJSONificationTest(ResolverTestCase):\n\n    def test_empty_worksheet_to_json(self):\n        worksheet = Worksheet()\n\n        worksheet_json = worksheet_to_json(worksheet)\n        self.assertEquals(\n            json.loads(worksheet_json),\n            {\n                \"_console_text\": \"\",\n                \"_usercode_error\": None,\n            }\n        )\n\n\n    @patch('sheet.worksheet.StringIO')\n    def test_worksheet_to_json_remembers_to_close_stringIO_stream(self, mock_stringio):\n        worksheet = Worksheet()\n        mock_stringio.return_value = Mock()\n        worksheet_to_json(worksheet)\n        self.assertCalledOnce(mock_stringio.return_value.close)\n\n\n    def test_worksheet_with_data_to_json(self):\n        self.maxDiff = None\n\n        worksheet = Worksheet()\n\n        worksheet.B29.formula = \"a constant\"\n        worksheet.B29.value = 56\n        worksheet.B29.formatted_value = \"fifty-six\"\n        worksheet.B29.error = \"b0rken\"\n\n        worksheet.C29.formula = \"another constant\"\n        worksheet.C29.value = [\"value\", \"is\", \"a\", \"list\"]\n        worksheet.C29.formatted_value = \"[the same list]\"\n\n        class UnJSONableObject(object):\n            def __str__(self):\n                return \"The result of str-ing the object\"\n        worksheet.D29.formula = None\n        worksheet.D29.value = UnJSONableObject()\n        worksheet.D29.formatted_value = \"The formatted object\"\n\n        worksheet.E29.formula = '=1 + 2'\n        worksheet.E29.value = 3\n        worksheet.E29.formatted_value = \"Three\"\n\n        worksheet._console_text = \"The console text\"\n        worksheet._usercode_error = { \"message\": \"The usercode error\", \"line\": 23 }\n\n        worksheet_json = worksheet_to_json(worksheet)\n\n        self.assertEquals(\n            json.loads(worksheet_json),\n            {\n                u\"2,29\" : {\n                    u\"formula\" : u\"a constant\",\n                    u\"value\" : 56,\n                    u\"formatted_value\": u\"fifty-six\",\n                    u\"error\": u\"b0rken\"\n                },\n\n                u\"3,29\" : {\n                    u\"formula\" : u\"another constant\",\n                    u\"value\" : [u\"value\", u\"is\", u\"a\", u\"list\"],\n                    u\"formatted_value\": u\"[the same list]\"\n                },\n\n                u\"4,29\" : {\n                    u\"formula\" : None,\n                    u\"formatted_value\": u\"The formatted object\",\n                },\n\n                u\"5,29\" : {\n                    u\"formula\" : u\"=1 + 2\",\n                    u\"python_formula\" : u\"1 + 2\",\n                    u\"value\": 3,\n                    u\"formatted_value\": u\"Three\",\n                },\n\n                u\"_console_text\": u\"The console text\",\n                u\"_usercode_error\": { u\"message\": u\"The usercode error\", u\"line\": 23 },\n            }\n        )\n\n\n    def test_dependencies_get_put_in_json_as_array_of_arrays(self):\n        self.maxDiff = None\n\n        worksheet = Worksheet()\n        worksheet.A1.dependencies = [(1, 2)]\n        worksheet._console_text = \"\"\n\n        worksheet_json = worksheet_to_json(worksheet)\n\n        self.assertEquals(\n            json.loads(worksheet_json),\n            {\n                u\"1,1\" : {\n                    u\"formula\" : None,\n                    u\"formatted_value\" : u\"\",\n                    u\"dependencies\" : [[1, 2]],\n                },\n\n                u\"_console_text\": u\"\",\n                u\"_usercode_error\": None,\n            }\n        )\n\n\n    def test_nan_values_are_ignored(self):\n        self.maxDiff = None\n\n        worksheet = Worksheet()\n        worksheet.A1.value = float('nan')\n        worksheet.A2.value = float('inf')\n        worksheet.A3.value = float('-inf')\n\n        worksheet_json = worksheet_to_json(worksheet)\n        roundtripped = json.loads(worksheet_json)\n        self.assertEquals(roundtripped[\"1,1\"]['formatted_value'], 'nan')\n        self.assertEquals(roundtripped[\"1,2\"]['formatted_value'], 'inf')\n        self.assertEquals(roundtripped[\"1,3\"]['formatted_value'], '-inf')\n        self.assertFalse('value' in roundtripped[\"1,1\"])\n        self.assertFalse('value' in roundtripped[\"1,2\"])\n        self.assertFalse('value' in roundtripped[\"1,3\"])\n\n\n    def test_empty_worksheet_from_json(self):\n        worksheet = worksheet_from_json(\n            json.dumps(\n                {\n                    \"_console_text\": \"\",\n                    \"_usercode_error\": None,\n                }\n            )\n        )\n\n        self.assertEquals(worksheet._console_text, \"\")\n        self.assertEquals(worksheet._usercode_error, None)\n        self.assertIsNotNone(worksheet._console_lock)\n\n\n    def test_worksheet_with_data_from_json(self):\n        worksheet = worksheet_from_json(\n            json.dumps(\n                {\n                    \"2,29\" : {\n                        \"formula\" : \"a formula\",\n                        \"value\" : 56,\n                        \"dependencies\" : [[4, 3], [2, 1]],\n                        \"formatted_value\": \"fifty-six\",\n                        \"error\": \"b0rken\"\n                    },\n\n                    \"3,29\" : {\n                        \"formula\" : \"another formula\",\n                        \"value\" : [\"value\", \"is\", \"a\", \"list\"],\n                        \"formatted_value\": \"[the same list]\"\n                    },\n\n                    \"4,29\" : {\n                        \"formula\" : None,\n                        \"formatted_value\": \"The formatted object\",\n                    },\n\n                    \"5,29\" : {\n                        \"formula\" : \"=2 + 4\",\n                        \"python_formula\" : \"2 + 4\",\n                        \"value\" : 6,\n                        \"formatted_value\": \"six\",\n                    },\n\n                    \"6,29\" : {\n                        \"formula\" : \"=I don't have a python formula. I don't want one.\",\n                        \"value\" : 7,\n                        \"formatted_value\": \"seven\",\n                    },\n\n                    \"_console_text\": \"The console text\",\n                    \"_usercode_error\": { \"message\": \"The usercode error\", \"line\": 23 },\n                }\n            )\n        )\n\n        self.assertEquals(worksheet.B29.formula, \"a formula\")\n        self.assertEquals(worksheet.B29.python_formula, None)\n        self.assertEquals(worksheet.B29.dependencies, [(4, 3), (2, 1)])\n        self.assertEquals(worksheet.B29.value, 56)\n        self.assertEquals(worksheet.B29.formatted_value, \"fifty-six\")\n        self.assertEquals(worksheet.B29.error, \"b0rken\")\n\n        self.assertEquals(worksheet.C29.formula, \"another formula\")\n        self.assertEquals(worksheet.C29.python_formula, None)\n        self.assertEquals(worksheet.C29.value, [\"value\", \"is\", \"a\", \"list\"])\n        self.assertEquals(worksheet.C29.formatted_value, \"[the same list]\")\n\n        self.assertEquals(worksheet.D29.formula, None)\n        self.assertEquals(worksheet.D29.python_formula, None)\n        self.assertEquals(worksheet.D29.value, undefined)\n        self.assertEquals(worksheet.D29.formatted_value, \"The formatted object\")\n\n        self.assertEquals(worksheet.E29.formula, \"=2 + 4\")\n        self.assertEquals(worksheet.E29.python_formula, \"2 + 4\")\n        self.assertEquals(worksheet.E29.value, 6)\n        self.assertEquals(worksheet.E29.formatted_value, \"six\")\n\n        self.assertEquals(worksheet.F29.formula, \"=I don't have a python formula. I don't want one.\")\n        self.assertEquals(worksheet.F29.python_formula, None)\n        self.assertEquals(worksheet.F29.value, 7)\n        self.assertEquals(worksheet.F29.formatted_value, \"seven\")\n\n        self.assertEquals(worksheet._console_text, \"The console text\")\n        self.assertEquals(worksheet._usercode_error, { \"message\": \"The usercode error\", \"line\": 23 })\n        self.assertIsNotNone(worksheet._console_lock)\n\n\n    @patch('sheet.worksheet.json')\n    def test_worksheet_from_json_uses_json(self, mock_json):\n        mock_json.loads.return_value = {}\n        worksheet_from_json('{}')\n        self.assertCalledOnce(mock_json.loads, '{}')\n\n\n\nclass WorksheetTest(unittest.TestCase):\n\n    def test_initialise(self):\n        ws = Worksheet()\n        self.assertEquals(dict(ws), {})\n        self.assertEquals(ws._console_text, '')\n        self.assertEquals(ws._usercode_error, None)\n        self.assertEquals(ws.name, None)\n\n\n    def test_repr(self):\n        ws = Worksheet()\n        self.assertEquals(repr(ws), '<Worksheet>')\n\n        ws.name = 'test worksheet'\n        self.assertEquals(repr(ws), '<Worksheet test worksheet>')\n\n    def test_equality(self):\n        ws1 = Worksheet()\n        ws2 = Worksheet()\n        ws2.A1.formula = 'a difference'\n        self.assertFalse(ws1==ws2)\n        self.assertTrue(ws1!=ws2)\n\n        ws3 = Worksheet()\n        self.assertTrue(ws1==ws3)\n        self.assertFalse(ws1!=ws3)\n\n        ws3.name = 'a different name!'\n        self.assertFalse(ws1==ws3)\n        self.assertTrue(ws1!=ws3)\n\n        nonWs = 1.2\n        self.assertFalse(ws1==nonWs)\n        self.assertTrue(ws1!=nonWs)\n\n\n    def test_append_console_text(self):\n        ws = Worksheet()\n        ws.add_console_text('a first error')\n        self.assertEquals(\n            ws._console_text,\n            '<span class=\"console_error_text\">a first error</span>')\n\n        ws.add_console_text('a second error\\noh noez!')\n        self.assertEquals(\n            ws._console_text,\n            '<span class=\"console_error_text\">a first error</span>'\n            '<span class=\"console_error_text\">a second error\\n'\n            'oh noez!</span>')\n\n        ws.add_console_text('not an error', log_type='output')\n        self.assertEquals(\n            ws._console_text,\n            '<span class=\"console_error_text\">a first error</span>'\n            '<span class=\"console_error_text\">a second error\\n'\n            'oh noez!</span>'\n            '<span class=\"console_output_text\">not an error</span>')\n\n        ws.add_console_text('A system timing report, for example :-)', log_type='system')\n        self.assertEquals(\n            ws._console_text,\n            '<span class=\"console_error_text\">a first error</span>'\n            '<span class=\"console_error_text\">a second error\\n'\n            'oh noez!</span>'\n            '<span class=\"console_output_text\">not an error</span>'\n            '<span class=\"console_system_text\">A system timing report, for example :-)</span>')\n\n        ws.add_console_text('<b></b>', log_type='output')\n        self.assertEquals(\n            ws._console_text,\n            '<span class=\"console_error_text\">a first error</span>'\n            '<span class=\"console_error_text\">a second error\\n'\n            'oh noez!</span>'\n            '<span class=\"console_output_text\">not an error</span>'\n            '<span class=\"console_system_text\">A system timing report, for example :-)</span>'\n            '<span class=\"console_output_text\">&lt;b&gt;&lt;/b&gt;</span>')\n\n\n    def test_to_location(self):\n        ws = Worksheet()\n\n        self.assertEquals( ws.to_location((1, 2)), (1, 2) )\n        self.assertEquals( ws.to_location((1L, 2L)), (1L, 2L) )\n        self.assertEquals( ws.to_location(('a', 2)), (1, 2) )\n        self.assertEquals( ws.to_location(('A', 2)), (1, 2) )\n        self.assertEquals( ws.to_location('a2'), (1, 2) )\n        self.assertEquals( ws.to_location('A2'), (1, 2) )\n\n        self.assertEquals( ws.to_location('A'), None )\n        self.assertEquals( ws.to_location('1A'), None )\n        self.assertEquals( ws.to_location((1, 'A')), None )\n        self.assertEquals( ws.to_location(123), None )\n        self.assertEquals( ws.to_location(object()), None )\n\n\n    def test_setitem_on_locations_should_accept_cell_instances(self):\n        ws = Worksheet()\n        ws.to_location = Mock(return_value=(1, 2))\n        cell = Cell()\n\n        ws[3, 4] = cell\n\n        self.assertEquals(ws.to_location.call_args_list, [(((3, 4),), {})])\n        self.assertEquals(ws.keys(), [(1, 2)])\n\n\n    def test_setitem_on_locations_should_reject_non_cell_instances(self):\n        ws = Worksheet()\n        ws.to_location = Mock(return_value=(1, 2))\n\n        expected_message_re = \"^Worksheet locations must be Cell objects\"\n        with self.assertRaisesRegexp(TypeError, expected_message_re):\n            ws[3, 4] = 123\n        self.assertEquals(ws.to_location.call_args_list, [(((3, 4),), {})])\n\n\n    def test_setitem_on_non_locations_raises_keyerror(self):\n        ws = Worksheet()\n        ws.to_location = Mock(return_value=None)\n\n        with self.assertRaisesRegexp(InvalidKeyError, \"^'random key' is not a valid cell location$\"):\n            ws['random key'] = 'sausages'\n\n\n    def test_getitem_creates_cells(self):\n        ws = Worksheet()\n        try:\n            ws[1, 2].value = Cell()\n        except KeyError:\n            self.fail('Did not create cell on request')\n\n\n    def test_get_item_does_not_create_cells_for_random_strings(self):\n        ws = Worksheet()\n        with self.assertRaisesRegexp(InvalidKeyError, \"^'name' is not a valid cell location$\"):\n            ws['name']\n        with self.assertRaisesRegexp(AttributeError, \"^'Worksheet' object has no attribute 'some_random_attribute'$\"):\n            ws.some_random_attribute\n\n\n    def test_getitem_should_use_to_location_result_if_it_is_not_none(self):\n        ws = Worksheet()\n        ws.to_location = Mock(return_value=(1, 2))\n\n        ws[3, 4].formula = \"hello\"\n\n        self.assertEquals(ws.to_location.call_args_list, [(((3, 4),), {})])\n        self.assertEquals(ws.keys(), [(1, 2)])\n        self.assertEquals(ws.values()[0].formula, \"hello\")\n\n\n    def test_getitem_should_use_original_key_if_to_location_gives_none(self):\n        ws = Worksheet()\n        ws.to_location = Mock(return_value=(3, 4))\n\n        ws[3, 4].formula = \"hello\"\n\n        self.assertEquals(ws.to_location.call_args_list, [(((3, 4),), {})])\n        self.assertEquals(ws.keys(), [(3, 4)])\n        self.assertEquals(ws.values()[0].formula, \"hello\")\n\n\n    def test_getattr_should_delegate_to_getitem(self):\n        ws = Worksheet()\n        ws.__getitem__ = Mock()\n\n        retval = ws.A1\n\n        self.assertEquals( retval, ws.__getitem__.return_value )\n        self.assertEquals( ws.__getitem__.call_args_list, [(('A1',), {})] )\n\n\n    @patch('sheet.worksheet.cell_name_to_coordinates')\n    def test_setattr_should_delegate_to_setitem_if_attr_name_is_valid_cell_name(\n        self, mock_name_to_coords\n    ):\n        def name_to_coords(name):\n            if name == 'A1':\n                return (2, 3)\n            else:\n                return None\n        mock_name_to_coords.side_effect = name_to_coords\n\n        ws = Worksheet()\n        ws.__setitem__ = Mock()\n\n        ws.A1 = 23\n\n        self.assertEquals( ws.__setitem__.call_args_list, [(((2, 3), 23), {})] )\n\n\n    @patch('sheet.worksheet.cell_name_to_coordinates', lambda _: None)\n    def test_setattr_should_not_delegate_to_setitem_if_attr_name_is_not_valid_cell_name(self):\n        ws = Worksheet()\n        ws.__setitem__ = Mock()\n\n        ws.A1 = 23\n\n        self.assertEquals( ws.__setitem__.call_args_list, [] )\n        self.assertEquals( ws.A1, 23 )\n\n\n    def test_set_cell_formula_with_value_should_update_internal_contents(self):\n        ws = Worksheet()\n        ws.set_cell_formula(1, 2, \"3\")\n        self.assertEquals(ws[1, 2].formula, '3')\n\n\n    def test_set_cell_formula_with_empty_string_should_clear_internal_contents_if_they_exist(self):\n        ws = Worksheet()\n        ws[1, 2].formula = \"=1\"\n        ws.set_cell_formula(1, 2, \"\")\n        self.assertFalse((1, 2) in ws)\n\n\n    def test_set_cell_formula_with_empty_string_should_do_nothing_if_no_preexisting_internal_contents(self):\n        ws = Worksheet()\n        ws.set_cell_formula(1, 2, \"\")\n        self.assertFalse((1, 2) in ws)\n\n\n    def test_clear_values_clears_values_and_formatted_values_and_errors(self):\n        ws = Worksheet()\n        ws[1, 2].formula = \"=1\"\n        ws[1, 2].python_formula = \"2\"\n        ws[1, 2].value = \"hello!\"\n        ws[1, 2].formatted_value = \"Guten Tag!\"\n        ws[1, 2].error = \"Goodness Gracious!\"\n\n        ws[2, 2].python_formula = \"1 + 1\"\n\n        ws.clear_values()\n\n        self.assertEquals(ws[1, 2].formula, \"=1\")\n        self.assertEquals(ws[1, 2].python_formula, \"2\")\n        self.assertEquals(ws[1, 2].value, undefined)\n        self.assertEquals(ws[1, 2].formatted_value, u'')\n        self.assertEquals(ws[1, 2].error, None)\n\n        self.assertEquals(ws[2, 2].python_formula, '1 + 1')\n\n\n    def test_clear_values_deletes_cells_with_no_formula(self):\n        ws = Worksheet()\n        ws[1, 2].formula = None\n        ws[1, 2].value = \"hello!\"\n        ws[1, 2].formatted_value = \"Guten Tag!\"\n        ws.clear_values()\n        self.assertFalse((1, 2) in ws)\n\n\n    def test_clear_values_deletes_cells_with_empty_formula(self):\n        ws = Worksheet()\n        ws[1, 2].formula = ''\n        ws[1, 2].value = \"hello!\"\n        ws[1, 2].formatted_value = \"Guten Tag!\"\n        ws.clear_values()\n        self.assertFalse((1, 2) in ws)\n\n\n    def test_iteration_yields_cells(self):\n        ws = Worksheet()\n        ws[1, 1].formula = 'A1'\n        ws[2, 4].formula = 'B4'\n        ws.name = 'any old name'\n\n        self.assertEquals(ws.items(), [((1, 1), ws[1, 1]), ((2, 4), ws[2, 4])])\n\n\n    def test_add_console_text_is_thread_safe(self):\n        ws = Worksheet()\n        num_threads = 100\n        num_chars = 1000\n        num_tries = 20\n\n        def get_console_text_adder(char_num):\n            def inner():\n                for _ in range(num_tries):\n                    ws.add_console_text(str(char_num) * num_chars)\n            return inner\n        threads = []\n        for i in range(num_threads):\n            threads.append(Thread(target=get_console_text_adder(i)))\n        for thread in threads:\n            thread.start()\n        for thread in threads:\n            thread.join()\n\n        for i in range(num_threads):\n            found_pos = -num_chars\n            for _ in range(num_tries):\n                found_pos = ws._console_text.find(str(i) * num_chars, found_pos + num_chars)\n                self.assertNotEqual(found_pos, -1, 'could not find all output for thread %s' % (str(i),))\n\n\n    def test_getting_bounds_on_empty_sheet_should_return_none(self):\n        ws = Worksheet()\n        self.assertEquals(ws.bounds, None)\n\n\n    def test_getting_bounds_with_one_cell_should_return_bounds(self):\n        ws = Worksheet()\n        ws[3, 5].value = \"Top right and bottom left!\"\n\n        self.assertEquals(type(ws.bounds), Bounds)\n        self.assertEquals(ws.bounds, (3, 5, 3, 5))\n\n\n    def test_getting_bounds_with_two_cells_should_return_bounds(self):\n        ws = Worksheet()\n        ws[5, 3].value = \"Top right\"\n        ws[3, 11].value = \"Bottom left\"\n\n        self.assertEquals(type(ws.bounds), Bounds)\n        self.assertEquals(ws.bounds, (3, 3, 5, 11))\n\n\n    def test_setting_bounds_should_fail(self):\n        ws = Worksheet()\n        with self.assertRaises(AttributeError):\n            ws.bounds = Bounds((1, 2, 3, 4))\n\n\n\nclass TestWorksheetCellRangeConstructor(ResolverTestCase):\n\n    def test_two_tuple_parameters(self):\n        ws = Worksheet()\n        cell_range = ws.cell_range((2, 3), (5, 4))\n        self.assertEquals(cell_range.left, 2)\n        self.assertEquals(cell_range.top, 3)\n        self.assertEquals(cell_range.right, 5)\n        self.assertEquals(cell_range.bottom, 4)\n\n    def test_single_string_parameter_uses_formula_notation(self):\n        ws = Worksheet()\n        cell_range = ws.cell_range('A2:C4')\n        self.assertEquals(cell_range.left, 1)\n        self.assertEquals(cell_range.top, 2)\n        self.assertEquals(cell_range.right, 3)\n        self.assertEquals(cell_range.bottom, 4)\n\n        try:\n            _ = ws.cell_range('A1:wibble')\n            self.fail('should raise ValueError')\n        except ValueError, e:\n            self.assertEquals(str(e), \"Invalid cell range 'A1:wibble'\")\n\n        try:\n            _ = ws.cell_range('wobblewibble')\n            self.fail('should raise ValueError')\n        except ValueError, e:\n            self.assertEquals(str(e), \"Invalid cell range 'wobblewibble'\")\n\n\n    def test_double_string_parameters_use_a1_notation(self):\n        ws = Worksheet()\n        cell_range = ws.cell_range('A2','C4')\n        self.assertEquals(cell_range.left, 1)\n        self.assertEquals(cell_range.top, 2)\n        self.assertEquals(cell_range.right, 3)\n        self.assertEquals(cell_range.bottom, 4)\n\n        try:\n            _ = ws.cell_range('wabble','C4')\n            self.fail('should raise ValueError')\n        except ValueError, e:\n            self.assertEquals(str(e), \"wabble is not a valid cell location\")\n\n        try:\n            _ = ws.cell_range('A1','wooble')\n            self.fail('should raise ValueError')\n        except ValueError, e:\n            self.assertEquals(str(e), \"wooble is not a valid cell location\")\n\n        try:\n            _ = ws.cell_range('weebble','beeble')\n            self.fail('should raise ValueError')\n        except ValueError, e:\n            self.assertEquals(str(e), \"Neither weebble nor beeble are valid cell locations\")\n\n\n    def test_mixed_parameters(self):\n        ws = Worksheet()\n        cell_range = ws.cell_range((10, 10),'C4')\n        self.assertEquals(cell_range.left, 3)\n        self.assertEquals(cell_range.top, 4)\n        self.assertEquals(cell_range.right, 10)\n        self.assertEquals(cell_range.bottom, 10)\n\n        try:\n            _ = ws.cell_range('wipple',(1,2))\n            self.fail('should raise ValueError')\n        except ValueError, e:\n            self.assertEquals(str(e), \"wipple is not a valid cell location\")\n\n        try:\n            _ = ws.cell_range((2, 2),'wapple')\n            self.fail('should raise ValueError')\n        except ValueError, e:\n            self.assertEquals(str(e), \"wapple is not a valid cell location\")\n\n\n\nclass BoundsTest(ResolverTestCase):\n\n    def test_bounds_acts_like_tuple(self):\n        bounds = Bounds((1, 2, 3, 4))\n        self.assertTrue(isinstance(bounds, tuple))\n        left, top, right, bottom = bounds\n        self.assertEquals((left, top, right, bottom), (1, 2, 3, 4))\n\n\n    def test_bounds_has_sweet_properties(self):\n        bounds = Bounds((1, 2, 3, 4))\n        self.assertEquals(bounds.left, 1)\n        self.assertEquals(bounds.top, 2)\n        self.assertEquals(bounds.right, 3)\n        self.assertEquals(bounds.bottom, 4)\n\n\n    def test_bounds_barfs_on_wrong_number_of_parameters(self):\n        self.assertRaises(ValueError, lambda : Bounds((1, 2, 3)))\n        self.assertRaises(ValueError, lambda : Bounds((1, 2, 3, 4, 5)))\n"
  },
  {
    "path": "dirigible/sheet/tests/utils/__init__.py",
    "content": ""
  },
  {
    "path": "dirigible/sheet/tests/utils/test_cell_name_utils.py",
    "content": "# Copyright (c) 2005-2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\ntry:\n    import unittest2 as unittest\nexcept ImportError:\n    import unittest\n\nimport sys\n\nfrom sheet.utils.cell_name_utils import (\n    cell_name_to_coordinates, cell_range_as_string_to_coordinates,\n    cell_ref_as_string_to_coordinates, _col_row_names_to_coordinates,\n    column_index_to_name, column_name_to_index, coordinates_to_cell_name,\n    MAX_COL)\n\n\nclass CellNameUtilsTest(unittest.TestCase):\n\n    def testColumnNameTo(self):\n        \"test column_name_to_index\"\n        self.assertEquals(column_name_to_index(\"A\"), 1, \"incorrect conversion\"  )\n        self.assertEquals(column_name_to_index(\"S\"), 19, \"incorrect conversion\"  )\n        self.assertEquals(column_name_to_index(\"Z\"), 26, \"incorrect conversion\"  )\n        self.assertEquals(column_name_to_index(\"AA\"), 27, \"incorrect conversion\"  )\n        self.assertEquals(column_name_to_index(\"aa\"), 27, \"incorrect conversion\"  )\n        self.assertEquals(column_name_to_index(\"AZ\"), 52, \"incorrect conversion\"  )\n        self.assertEquals(column_name_to_index(\"aZ\"), 52, \"incorrect conversion\"  )\n        self.assertEquals(column_name_to_index(\"BA\"), 53, \"incorrect conversion\"  )\n        self.assertEquals(column_name_to_index(\"Ba\"), 53, \"incorrect conversion\"  )\n        self.assertEquals(column_name_to_index(\"mjJ\"), 9058, \"incorrect conversion\"  )\n        self.assertEquals(column_name_to_index(\"MJJ\"), 9058, \"incorrect conversion\"  )\n        self.assertEquals(column_name_to_index(\"ZZZ\"), 18278, \"incorrect conversion\")\n\n        self.assertIsNone(column_name_to_index(\"AAAA\"), \"invalid column not None\")\n        self.assertIsNone(column_name_to_index(\"HELLO3\"), \"invalid column not None\")\n        self.assertIsNone(column_name_to_index(\"SHEET\"), \"invalid column not None\")\n        self.assertIsNone(column_name_to_index(\"@\"), \"invalid column not None\")\n        self.assertIsNone(column_name_to_index(\"33\"), \"invalid column not None\")\n        self.assertIsNone(column_name_to_index(\"FXSHRXX\"), \"invalid column not None (column too large for 32 bit int)\")\n\n\n    def testColumnIndexTo(self):\n        \"test column_index_to_name\"\n        self.assertEquals(column_index_to_name(1), \"A\", \"incorrect conversion\")\n        self.assertEquals(column_index_to_name(19), \"S\", \"incorrect conversion\")\n        self.assertEquals(column_index_to_name(26), \"Z\", \"incorrect conversion\")\n        self.assertEquals(column_index_to_name(27), \"AA\", \"incorrect conversion\")\n        self.assertEquals(column_index_to_name(52), \"AZ\", \"incorrect conversion\")\n        self.assertEquals(column_index_to_name(9058), \"MJJ\", \"incorrect conversion\")\n        self.assertEquals(column_index_to_name(18278), \"ZZZ\", \"incorrect conversion\")\n        self.assertIsNone(column_index_to_name(18279), \"invalid column not None\")\n        self.assertIsNone(column_index_to_name(8826682), \"invalid column not None\")\n        self.assertIsNone(column_index_to_name(sys.maxint+1), \"column too large for 32 bit int not None\")\n\n\n    def testColRowNames(self):\n        \"test col row names to coordinates\"\n        self.assertEquals(_col_row_names_to_coordinates(\"A\", \"1\"), (1, 1), \"To coordinates test 1 failed\")\n        self.assertEquals(_col_row_names_to_coordinates(\"B\", \"1\"), (2, 1), \"To coordinates test 2 failed\")\n        self.assertEquals(_col_row_names_to_coordinates(\"C\", \"5\"), (3, 5), \"To coordinates test 3 failed\")\n        self.assertEquals(_col_row_names_to_coordinates(\"aC\", \"100\"), (29, 100), \"To coordinates test 4 failed\")\n        self.assertIsNone(_col_row_names_to_coordinates(\"A\", None), \"Invalid coordinates\")\n        self.assertIsNone(_col_row_names_to_coordinates(\"FXSHRXX\", None), \"Invalid coordinates\")\n        self.assertIsNone(_col_row_names_to_coordinates(None, None), \"Invalid coordinates\")\n\n\n    def testCoordinatesToCell(self):\n        \"test coordinates_to_cell_name\"\n        self.assertEquals(coordinates_to_cell_name(1, 1), \"A1\", \"To name test 1 failed\")\n        self.assertEquals(coordinates_to_cell_name(2, 1), \"B1\", \"To name test 2 failed\")\n        self.assertEquals(coordinates_to_cell_name(3, 5), \"C5\", \"To name test 3 failed\")\n        self.assertEquals(coordinates_to_cell_name(29, 100), \"AC100\", \"To name test 4 failed\")\n        self.assertEquals(coordinates_to_cell_name(13057, 1), \"SHE1\", \"To name test 4 failed\")\n\n\n    def testCoordinatesToCell2(self):\n        \"test coordinates_to_cell_name with absolution\"\n        self.assertEquals(coordinates_to_cell_name(1, 1, colAbsolute=True), \"$A1\", \"To name test 1 failed\")\n        self.assertEquals(coordinates_to_cell_name(2, 1, rowAbsolute=True), \"B$1\", \"To name test 2 failed\")\n        self.assertEquals(coordinates_to_cell_name(3, 5, colAbsolute=True, rowAbsolute=True), \"$C$5\",\n                          \"To name test 3 failed\")\n\n\n    def test_coordinates_to_cell_name_bad(self):\n        self.assertIsNone(coordinates_to_cell_name(0, 1), \"bad\")\n        self.assertIsNone(coordinates_to_cell_name(1, 0), \"bad\")\n        self.assertIsNone(coordinates_to_cell_name(0, 0), \"bad\")\n        self.assertIsNone(coordinates_to_cell_name(1, -1), \"bad\")\n        self.assertIsNone(coordinates_to_cell_name(-1, 1), \"bad\")\n        self.assertIsNone(coordinates_to_cell_name(MAX_COL + 1, 1), \"bad\")\n\n\n    def testCellNameTo(self):\n        \"test cell_name_to_coordinates\"\n        self.assertEquals(cell_name_to_coordinates(\"A1\"), (1, 1), \"To coordinates test 1 failed\")\n        self.assertEquals(cell_name_to_coordinates(\"b1\"), (2, 1), \"To coordinates test 2 failed\")\n        self.assertEquals(cell_name_to_coordinates(\"C5\"), (3, 5), \"To coordinates test 3 failed\")\n        self.assertEquals(cell_name_to_coordinates(\"ac100\"), (29, 100), \"To coordinates test 4 failed\")\n        self.assertEquals(cell_name_to_coordinates(\"A1   \"), (1, 1), \"To coordinates test 5 failed\")\n        self.assertEquals(cell_name_to_coordinates(\"She1\"), (13057, 1), \"To coordinates test 5 failed\")\n\n        self.assertIsNone(cell_name_to_coordinates(\"56G\"), \"To coordinates test 5 failed\")\n        self.assertIsNone(cell_name_to_coordinates(\"YTGDCJK\"), \"To coordinates test 6 failed\")\n        self.assertIsNone(cell_name_to_coordinates(\"54688\"), \"To coordinates test 7 failed\")\n        self.assertIsNone(cell_name_to_coordinates(\"\"), \"To coordinates test 8 failed\")\n        self.assertIsNone(cell_name_to_coordinates(\"W0mbat\"), \"To coordinates test 9 failed\")\n        self.assertIsNone(cell_name_to_coordinates(\"    \"), \"To coordinates test 10 failed\")\n        self.assertIsNone(cell_name_to_coordinates(\";-)\"), \"To coordinates test 11 failed\")\n        self.assertIsNone(cell_name_to_coordinates(None), \"To coordinates test 12 failed\")\n        self.assertIsNone(cell_name_to_coordinates(\"FXSHRXX1\"), \"Invalid column name\")\n        self.assertIsNone(cell_name_to_coordinates(\"A9223372036854775808\"), \"Invalid row number\")\n\n\n    def testAbsoluteCellName(self):\n        \"test absolute cell_name_to_coordinates\"\n        self.assertEquals(cell_name_to_coordinates(\"$A1\"), (1, 1), \"To coordinates test 1 failed\")\n        self.assertEquals(cell_name_to_coordinates(\"A$1\"), (1, 1), \"To coordinates test 1 failed\")\n        self.assertEquals(cell_name_to_coordinates(\"$A$1\"), (1, 1), \"To coordinates test 1 failed\")\n\n        self.assertEquals(cell_name_to_coordinates(\"$b1\"), (2, 1), \"To coordinates test 2 failed\")\n        self.assertEquals(cell_name_to_coordinates(\"b$1\"), (2, 1), \"To coordinates test 2 failed\")\n        self.assertEquals(cell_name_to_coordinates(\"$b$1\"), (2, 1), \"To coordinates test 2 failed\")\n\n        self.assertEquals(cell_name_to_coordinates(\"$C5\"), (3, 5), \"To coordinates test 3 failed\")\n        self.assertEquals(cell_name_to_coordinates(\"C$5\"), (3, 5), \"To coordinates test 3 failed\")\n        self.assertEquals(cell_name_to_coordinates(\"$C$5\"), (3, 5), \"To coordinates test 3 failed\")\n\n        self.assertEquals(cell_name_to_coordinates(\"$ac100\"), (29, 100), \"To coordinates test 4 failed\")\n        self.assertEquals(cell_name_to_coordinates(\"ac$100\"), (29, 100), \"To coordinates test 4 failed\")\n        self.assertEquals(cell_name_to_coordinates(\"$ac$100\"), (29, 100), \"To coordinates test 4 failed\")\n\n        self.assertEquals(cell_name_to_coordinates(\"$A1   \"), (1, 1), \"To coordinates test 5 failed\")\n        self.assertEquals(cell_name_to_coordinates(\"A$1   \"), (1, 1), \"To coordinates test 5 failed\")\n        self.assertEquals(cell_name_to_coordinates(\"$A$1   \"), (1, 1), \"To coordinates test 5 failed\")\n\n        self.assertEquals(cell_name_to_coordinates(\"$She1\"), (13057, 1), \"To coordinates test 5 failed\")\n        self.assertEquals(cell_name_to_coordinates(\"She$1\"), (13057, 1), \"To coordinates test 5 failed\")\n        self.assertEquals(cell_name_to_coordinates(\"$She$1\"), (13057, 1), \"To coordinates test 5 failed\")\n\n        self.assertIsNone(cell_name_to_coordinates(\"$56G\"), \"To coordinates test 5 failed\")\n        self.assertIsNone(cell_name_to_coordinates(\"56$G\"), \"To coordinates test 5 failed\")\n        self.assertIsNone(cell_name_to_coordinates(\"$56$G\"), \"To coordinates test 5 failed\")\n\n        self.assertIsNone(cell_name_to_coordinates(\"$$A1\"), \"To coordinates test 6 failed\")\n        self.assertIsNone(cell_name_to_coordinates(\"A$$1\"), \"To coordinates test 6 failed\")\n        self.assertIsNone(cell_name_to_coordinates(\"$YTGDCJK\"), \"To coordinates test 6 failed\")\n        self.assertIsNone(cell_name_to_coordinates(\"546$88\"), \"To coordinates test 7 failed\")\n        self.assertIsNone(cell_name_to_coordinates(\"\"), \"To coordinates test 8 failed\")\n        self.assertIsNone(cell_name_to_coordinates(\"W0m$bat\"), \"To coordinates test 9 failed\")\n        self.assertIsNone(cell_name_to_coordinates(\" $   \"), \"To coordinates test 10 failed\")\n        self.assertIsNone(cell_name_to_coordinates(\";-$)\"), \"To coordinates test 11 failed\")\n        self.assertIsNone(cell_name_to_coordinates(None), \"To coordinates test 12 failed\")\n        self.assertIsNone(cell_name_to_coordinates(\"$FXSHRXX$1\"), \"Invalid column name\")\n        self.assertIsNone(cell_name_to_coordinates(\"FXSHR$XX1\"), \"Invalid column name\")\n        self.assertIsNone(cell_name_to_coordinates(\"A$9223372036854775808\"), \"Invalid row number\")\n\n\n    def test_cell_ref_as_string_to_coordinates(self):\n        self.assertEquals(cell_ref_as_string_to_coordinates('1, 2'), (1, 2))\n        self.assertEquals(cell_ref_as_string_to_coordinates(' 10 , 2'), (10, 2))\n        self.assertEquals(cell_ref_as_string_to_coordinates('(1, 2)'), (1, 2))\n        self.assertEquals(cell_ref_as_string_to_coordinates(' (10 , 2)'), (10, 2))\n        self.assertEquals(cell_ref_as_string_to_coordinates('J11'), (10, 11))\n        self.assertIsNone(cell_ref_as_string_to_coordinates('J, 11'))\n        self.assertIsNone(cell_ref_as_string_to_coordinates('blurgle'))\n\n\n    def test_cell_range_as_string_to_coordinates(self):\n        self.assertEquals(cell_range_as_string_to_coordinates('A2:C4'), ((1, 2), (3, 4)))\n        self.assertEquals(cell_range_as_string_to_coordinates('AA10000:ZZ99999'),\n                          ((27, 10000), (26 * 27, 99999)) )\n\n        self.assertEquals(cell_range_as_string_to_coordinates('C4:A2'), ((3, 4), (1, 2)))\n\n        self.assertIsNone(cell_range_as_string_to_coordinates('A0:C4'))\n        self.assertIsNone(cell_range_as_string_to_coordinates('A2:C0'))\n        self.assertIsNone(cell_range_as_string_to_coordinates('A2::C4'))\n        self.assertIsNone(cell_range_as_string_to_coordinates(':A2:C4'))\n        self.assertIsNone(cell_range_as_string_to_coordinates('AA:C4:'))\n        self.assertIsNone(cell_range_as_string_to_coordinates('22:C4'))\n        self.assertIsNone(cell_range_as_string_to_coordinates('A2:CC'))\n        self.assertIsNone(cell_range_as_string_to_coordinates('A2:44'))\n\n\n"
  },
  {
    "path": "dirigible/sheet/tests/utils/test_interruptable_thread.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\r\n# See LICENSE.md\r\n#\r\nimport sys\r\nimport time\r\n\r\ntry:\r\n    import unittest2 as unittest\r\nexcept ImportError:\r\n    import unittest\r\n\r\nfrom sheet.utils.interruptable_thread import InterruptableThread\r\n\r\nclass TestInterruptableThread(unittest.TestCase):\r\n\r\n    def test_interruptable_thread_is(self):\r\n        def sleep_lots():\r\n            # Setting stderr to None keeps test output from\r\n            # having random stack traces inserted\r\n            sys.stderr = None\r\n            start = time.clock()\r\n            while time.clock() - start < 10:\r\n                pass\r\n            self.fail('thread not interrupted')\r\n\r\n        it = InterruptableThread(target=sleep_lots)\r\n        it.start()\r\n        it.join(5)\r\n        self.assertTrue(it.isAlive())\r\n        it.interrupt()\r\n        time.sleep(1)\r\n        self.assertFalse(it.isAlive())\r\n"
  },
  {
    "path": "dirigible/sheet/tests/utils/test_string_utils.py",
    "content": "# -*- coding: utf-8 -*-\n# Copyright (c) 2005-2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\n\ntry:\n    import unittest2 as unittest\nexcept ImportError:\n    import unittest\n\nfrom sheet.utils.string_utils import (\n    correct_case, double_quote_repr_string, get_lstripped_part, get_rstripped_part\n)\n\n\nclass StringUtilsTest(unittest.TestCase):\n\n    def test_get_rstripped_part(self):\n        self.assertEquals(get_rstripped_part(\"foo     \"), \"     \", \"get_r_stripped_part broken\")\n        self.assertEquals(get_rstripped_part(\"   foo \"), \" \", \"get_r_stripped_part broken\")\n        self.assertEquals(get_rstripped_part(\"\"), \"\", \"get_r_stripped_part broken\")\n\n\n    def test_get_lstripped_part(self):\n        self.assertEquals(get_lstripped_part(\"     foo\"), \"     \", \"get_l_stripped_part broken\")\n        self.assertEquals(get_lstripped_part(\"foo \"), \"\", \"get_l_stripped_part broken\")\n        self.assertEquals(get_lstripped_part(\" foo   \"), \" \", \"get_l_stripped_part broken\")\n        self.assertEquals(get_lstripped_part(\"\"), \"\", \"get_l_stripped_part broken\")\n\n\n    def test_double_quote_repr(self):\n        \"test double_quote_repr_string\"\n        self.assertEquals(double_quote_repr_string('what\\'s \"this\"'), '\"what\\'s \\\\\"this\\\\\"\"', \"didn't work\")\n        self.assertEquals(double_quote_repr_string(''), '\"\"', \"didn't work\")\n\n\n    def testCorrectCase(self):\n        self.assertEquals(correct_case(\"a\", [\"A\", \"B\"]), \"A\", \"case not corrected\")\n        self.assertEquals(correct_case(\"a\", [\"C\", \"B\"]), \"a\", \"changed despite no matches\")\n        self.assertEquals(correct_case(\"flippertigibbet\", [\"A\", \"B\", \"FlipperTiGibbet\"]), \"FlipperTiGibbet\", \"case not corrected\")\n\n"
  },
  {
    "path": "dirigible/sheet/ui_jsonifier.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\nimport simplejson as json\n\n\ndef sheet_to_ui_json_meta_data(sheet, worksheet):\n    result = {\n        'name':sheet.name,\n        'width':sheet.width,\n        'height':sheet.height,\n    }\n    if sheet.column_widths:\n        result['column_widths'] = sheet.column_widths\n    if worksheet._console_text:\n        result['console_text'] = worksheet._console_text\n    if worksheet._usercode_error:\n        result['usercode_error'] = {\n            'message' : worksheet._usercode_error['message'],\n            'line' : str(worksheet._usercode_error['line'])\n        }\n    return json.dumps(result)\n\n\ndef sheet_to_ui_json_grid_data(worksheet, rnge):\n    result = {}\n    left, topmost, right, bottom = rnge\n    result['left'] = left\n    result['topmost'] = topmost\n    result['right'] = right\n    result['bottom'] = bottom\n    for (col, row), cell in worksheet.items():\n        if rnge is not None:\n            if (\n                col < left or col > right or\n                row < topmost or row > bottom\n            ):\n                continue\n        cell_content = {}\n        if cell.formula is not None:\n            cell_content['formula'] = cell.formula\n        if cell.formatted_value and not cell.error:\n            cell_content['formatted_value'] = cell.formatted_value\n        if cell.error:\n            cell_content['error'] = cell.error\n        if cell_content != {}:\n            row_dict = result.setdefault(row, {})\n            row_dict[col] = cell_content\n    return json.dumps(result)\n"
  },
  {
    "path": "dirigible/sheet/urls.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\nfrom django.conf.urls import *\n\nfrom .views import (\n    calculate, clear_cells, clipboard, copy_sheet, export_csv, get_json_grid_data_for_ui,\n    get_json_meta_data_for_ui, import_csv, import_xls, page, set_cell_formula,\n    set_column_widths, set_sheet_name, set_sheet_security_settings,\n    set_sheet_usercode\n)\n\n\nURL_BASE = r'^(?P<sheet_id>\\d+)/'\n\n# Included from users URLs file, so we already have a\n# username parameter before any parameters we capture\n# here.\nurlpatterns = patterns('',\n\n    url(\n        '%s$' % URL_BASE,\n        page,\n        name=\"sheet_page\"\n    ),\n\n\n    url(\n        '%scalculate/$' % URL_BASE,\n        calculate,\n        name=\"sheet_calculate\"\n    ),\n\n\n    url(\n        '%sget_json_grid_data_for_ui/$' % URL_BASE,\n        get_json_grid_data_for_ui,\n        name=\"sheet_get_json_grid_data_for_ui\"\n    ),\n\n    url(\n        '%sget_json_meta_data_for_ui/$' % URL_BASE,\n        get_json_meta_data_for_ui,\n        name=\"sheet_get_json_meta_data_for_ui\"\n    ),\n\n\n    url(\n        \"%simport_csv/$\" % URL_BASE,\n        import_csv,\n        name=\"sheet_import_csv\"\n    ),\n\n    url(\n        \"%sexport_csv/(?P<csv_format>excel|unicode)/$\" % URL_BASE,\n        export_csv,\n        name=\"sheet_export_csv\"\n    ),\n\n    url(\n        \"import_xls/$\",\n        import_xls,\n        name=\"sheet_import_xls\"\n    ),\n\n\n    url(\n        \"%scopy_sheet/$\" % URL_BASE,\n        copy_sheet,\n        name=\"sheet_copy_sheet\"\n    ),\n\n\n    url(\n        '%sset_cell_formula/$' % URL_BASE,\n        set_cell_formula,\n        name=\"sheet_set_cell_formula\"\n    ),\n\n    url(\n        '%sset_column_widths/$' % URL_BASE,\n        set_column_widths,\n        name=\"sheet_set_column_widths\"\n    ),\n\n    url(\n        \"%sset_sheet_name/$\" % URL_BASE,\n        set_sheet_name,\n        name=\"sheet_set_sheet_name\"\n    ),\n\n    url(\n        '%sset_sheet_usercode/$' % URL_BASE,\n        set_sheet_usercode,\n        name=\"sheet_set_sheet_usercode\"\n    ),\n\n    url(\n        '%sset_sheet_security_settings/$' % URL_BASE,\n        set_sheet_security_settings,\n        name=\"sheet_set_sheet_security_settings\"\n    ),\n\n\n    url(\n        '%scut/$' % URL_BASE,\n        lambda *args, **kwargs: clipboard(action='cut', *args, **kwargs),\n        name=\"sheet_cut\"\n    ),\n\n    url(\n        '%scopy/$' % URL_BASE,\n        lambda *args, **kwargs: clipboard(action='copy', *args, **kwargs),\n        name=\"sheet_copy\"\n    ),\n\n    url(\n        '%spaste/$' % URL_BASE,\n        lambda *args, **kwargs: clipboard(action='paste', *args, **kwargs),\n        name=\"sheet_paste\"\n    ),\n\n\n    url(\n        '%sclear_cells/$' % URL_BASE,\n        clear_cells,\n        name=\"sheet_clear_cells\"\n    ),\n\n\n    url(\n        r'%sv0\\.1/' % (URL_BASE,),\n        include('sheet.urls_api_0_1'),\n    ),\n\n)\n"
  },
  {
    "path": "dirigible/sheet/urls_api_0_1.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\nfrom django.conf.urls import *\n\nfrom .views_api_0_1 import calculate_and_get_json_for_api\n\n\nurlpatterns = patterns('',\n\n    url(\n        r'^json/$',\n        calculate_and_get_json_for_api,\n        name='api_v0.1_get_sheet_json'\n    ),\n)\n"
  },
  {
    "path": "dirigible/sheet/utils/__init__.py",
    "content": ""
  },
  {
    "path": "dirigible/sheet/utils/cell_name_utils.py",
    "content": "# Copyright (c) 2005-2009 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\nimport sys\n\n# We allow 3 letter column names\nMAX_COL = (26 ** 3) + (26 ** 2) + 26\n\ndef column_name_to_index(colName):\n    if not colName.isalpha():\n        return None\n\n    result = 0\n    for letter in colName.upper():\n        result *= 26\n        result += ord(letter) - ord(\"A\") + 1\n    if result > MAX_COL:\n        return None\n    return result\n\ndef column_index_to_name(index):\n    if index > MAX_COL:\n        return None\n\n    result = \"\"\n    while index > 0:\n        thisChar = chr((index - 1) % 26 + ord(\"A\"))\n        index = (index - 1) // 26\n        result = thisChar + result\n\n    return result\n\n\ndef _col_row_names_to_coordinates(col, row):\n    if not row or row == \"0\" or not col:\n        return None\n    colIndex = column_name_to_index(col)\n    rowIndex = int(row)\n    if not colIndex or rowIndex > sys.maxint:\n        return None\n    return (colIndex, rowIndex)\n\n\ndef coordinates_to_cell_name(col, row, colAbsolute=False, rowAbsolute=False):\n    if col < 1 or col > MAX_COL or row < 1:\n        return None\n    colPrefix = '$' * colAbsolute\n    rowPrefix = '$' * rowAbsolute\n    return colPrefix + column_index_to_name(col) + rowPrefix + str(row)\n\n\ndef cell_name_to_coordinates(cellName):\n    if not cellName:\n        return None\n\n    if cellName.startswith(\"$\"):\n        cellName = cellName[1:]\n\n    row = \"\"\n    col = \"\"\n\n    dollarSeen = False\n\n    for c in cellName.rstrip():\n        if c.isalpha():\n            if row:\n                return None\n            else:\n                col += c\n        elif c == \"$\":\n            if dollarSeen or not col or row:\n                return None\n            dollarSeen = True\n        elif c.isdigit():\n            if not col:\n                return None\n            else:\n                row += c\n        else:\n            return None\n\n    return _col_row_names_to_coordinates(col, row)\n\n\ndef cell_ref_as_string_to_coordinates(cell_ref):\n    cell_ref = cell_ref.replace('(', '').replace(')', '')\n    if ',' in cell_ref:\n        try:\n            return tuple(map(int, cell_ref.split(',')))\n        except ValueError:\n            return None\n    else:\n        return cell_name_to_coordinates(cell_ref)\n\n\n\ndef cell_range_as_string_to_coordinates(cell_range_string):\n    parts = cell_range_string.split(':')\n    if len(parts) != 2:\n        return None\n    str_start, str_end = parts\n    start = cell_name_to_coordinates(str_start)\n    end = cell_name_to_coordinates(str_end)\n    if start is None or end is None:\n        return None\n    else:\n        return start, end\n"
  },
  {
    "path": "dirigible/sheet/utils/interruptable_thread.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\r\n# See LICENSE.md\r\n#\r\nfrom ctypes import c_long, py_object, pythonapi\r\nfrom threading import _active, Thread\r\n\r\nclass TimeoutException(Exception):\r\n    pass\r\n\r\nclass InterruptableThread(Thread):\r\n\r\n    def _get_thread_id(self):\r\n        if hasattr(self, \"_thread_id\"):\r\n            return self._thread_id\r\n        for tid, tobj in _active.items():\r\n            if tobj is self:\r\n                self._thread_id = tid\r\n                return tid\r\n\r\n\r\n    def interrupt(self):\r\n        if not self.isAlive():\r\n            return\r\n        tid = self._get_thread_id()\r\n        threads_affected = pythonapi.PyThreadState_SetAsyncExc(c_long(tid), py_object(TimeoutException))\r\n        if threads_affected != 1:\r\n            # if it returns a number greater than one, you're in trouble,\r\n            # and you should call it again with exc=NULL to revert the effect\r\n            pythonapi.PyThreadState_SetAsyncExc(c_long(tid), None)\r\n            raise SystemError(\"PyThreadState_SetAsyncExc failed\")\r\n"
  },
  {
    "path": "dirigible/sheet/utils/string_utils.py",
    "content": "# Copyright (c) 2005-2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\ndef get_rstripped_part(string):\n    strippedString = string.rstrip()\n    whitespace = string[len(strippedString):]\n    return whitespace\n\n\ndef get_lstripped_part(string):\n    strippedString = string.lstrip()\n    whitespace = string[:-len(strippedString)]\n    return whitespace\n\n\ndef double_quote_repr_string(inString):\n    result = repr(inString)[1:-1]\n    result = result.replace('\"', '\\\\\"')\n    result = result.replace(\"\\\\'\", \"'\")\n    return \"\\\"%s\\\"\" % result\n\n\ndef correct_case(candidate, potentialMatches):\n    for match in potentialMatches:\n        if match.lower() == candidate.lower():\n            return match\n    return candidate\n"
  },
  {
    "path": "dirigible/sheet/views.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\nfrom functools import wraps\nimport json\nimport os\nfrom os.path import splitext\nfrom tempfile import mkstemp\nimport xlrd\n\nfrom django.contrib.auth.decorators import login_required\nfrom django.core.mail import send_mail\nfrom django.core.urlresolvers import reverse\nfrom django.db import transaction\nfrom django.db.models import Q\nfrom django.http import HttpResponse, HttpResponseForbidden, HttpResponseRedirect\nfrom django.shortcuts import render, get_object_or_404\nfrom django.template.context import RequestContext\nfrom django.views.decorators.cache import never_cache\nfrom django.template.loader import get_template, render_to_string\nfrom django.template import Context\nfrom django.utils.html import escape\n\nfrom .forms import ImportCSVForm\nfrom .models import Clipboard, Sheet, copy_sheet_to_user\nfrom .ui_jsonifier import (\n    sheet_to_ui_json_grid_data, sheet_to_ui_json_meta_data\n)\nfrom .worksheet import worksheet_to_csv\nfrom .importer import (\n    DirigibleImportError, worksheet_from_csv, worksheet_from_excel\n)\nfrom user.models import AnonymousUser\n\n\ndef fetch_users_sheet(view):\n    def _fetch_users_sheet_if_permitted(request, username, sheet_id, *args, **kwargs):\n        sheet = get_object_or_404(Sheet, pk=sheet_id, owner__username=username)\n        if sheet.owner != request.user:\n            if not request.user.is_staff:\n                return HttpResponseForbidden(render_to_string(\"403.html\"))\n        return view(request, sheet, *args, **kwargs)\n    return _fetch_users_sheet_if_permitted\n\n\ndef fetch_users_or_public_sheet(view):\n\n    def _fetch_sheet_if_permitted(request, username, sheet_id, *args, **kwargs):\n        sheet = get_object_or_404(Sheet, pk=sheet_id, owner__username=username)\n        if not sheet.is_public:\n            if isinstance(request.user, AnonymousUser):\n                sheet_url = reverse('sheet_page', args=(username, sheet_id))\n                return HttpResponseRedirect('/login?next=/%s' % (sheet_url,))\n            elif sheet.owner != request.user and not request.user.is_staff:\n                return HttpResponseForbidden(render_to_string(\"403.html\"))\n\n        sheet.public_view_mode = (sheet.owner != request.user)\n\n        return view(request, sheet, *args, **kwargs)\n\n    return _fetch_sheet_if_permitted\n\n\n\ndef rollback_on_exception(view):\n    @wraps(view)\n    def _rollback_on_exception(*args, **kwargs):\n        transaction.set_autocommit(False)\n        try:\n            return view(*args, **kwargs)\n        except:\n            transaction.rollback()\n            raise\n        finally:\n            transaction.set_autocommit(True)\n    return _rollback_on_exception\n\n\ndef update_sheet_with_version_check(sheet, **kwargs):\n    query = Q(id=sheet.id) & Q(version=sheet.version)\n    sheets_updated = Sheet.objects.filter(query).update(version=sheet.version + 1, **kwargs)\n    return sheets_updated != 0\n\n\n@login_required\ndef import_xls(request, username):\n    if request.user.username != username:\n        return HttpResponseForbidden(render_to_string(\"403.html\"))\n\n    handle, filename = mkstemp()\n    try:\n        os.write(handle, request.FILES['file'].read())\n        wb = xlrd.open_workbook(filename)\n        for xl_sheet in wb.sheets():\n            if xl_sheet.nrows > 0 and xl_sheet.ncols > 0:\n                name = '%s - %s' % (\n                    splitext(request.FILES['file'].name)[0],\n                    xl_sheet.name\n                )\n                sheet = Sheet(owner=request.user, name=name)\n                sheet.jsonify_worksheet(worksheet_from_excel(xl_sheet))\n                sheet.save()\n\n                try:\n                    calculate(request, sheet.owner.username, sheet.id)\n                except:\n                    pass\n\n    except Exception:\n        return render(\n            request,\n            'import_xls_error.html',\n            {},\n            context_instance=RequestContext(request)\n        )\n    finally:\n        os.close(handle)\n        os.unlink(filename)\n    return HttpResponseRedirect('/')\n\n\n@fetch_users_or_public_sheet\ndef export_csv(request, sheet, csv_format):\n    if csv_format == 'unicode':\n        encoding = 'utf-8'\n    else:\n        encoding = 'windows-1252'\n\n    try:\n        content = worksheet_to_csv(\n            sheet.unjsonify_worksheet(),\n            encoding=encoding\n        )\n    except UnicodeEncodeError:\n        return render(\n            request,\n            'export_csv_error.html',\n            {'sheet': sheet},\n            context_instance=RequestContext(request)\n        )\n\n    response = HttpResponse(content, content_type='text/csv')\n    response['Content-Disposition'] = 'attachment; filename=%s.csv' % (sheet.name,)\n    response['Content-Length'] = len(content)\n    return response\n\n\n@login_required\n@fetch_users_sheet\ndef import_csv(request, sheet):\n    def error_response():\n        return render(\n            request,\n            'import_csv_error.html',\n            {'sheet': sheet},\n            context_instance=RequestContext(request)\n        )\n\n    form = ImportCSVForm(request.POST, request.FILES)\n    if not form.is_valid():\n        return error_response()\n\n    worksheet = sheet.unjsonify_worksheet()\n    csv_file = request.FILES['file']\n    column = form.cleaned_data['column']\n    row = form.cleaned_data['row']\n    excel_file_encoding = request.POST['csv_encoding']=='excel'\n    try:\n        worksheet = worksheet_from_csv(\n            worksheet, csv_file, column, row, excel_file_encoding\n        )\n    except DirigibleImportError:\n        return error_response()\n\n    sheet.jsonify_worksheet(worksheet)\n    if update_sheet_with_version_check(sheet, contents_json=sheet.contents_json):\n        calculate(request, sheet.owner.username, sheet.id)\n        return HttpResponseRedirect(reverse('sheet_page',kwargs={\n                'username' : request.user.username,\n                'sheet_id' : sheet.id,\n        }))\n    else:\n        return HttpResponse('FAILED')\n\n\n@login_required\n@fetch_users_or_public_sheet\ndef copy_sheet(request, sheet):\n    copied_sheet = copy_sheet_to_user(sheet, request.user)\n    return HttpResponseRedirect(reverse('sheet_page', kwargs={\n        'username': request.user.username,\n        'sheet_id': copied_sheet.id\n    }))\n\n\n@login_required\ndef new_sheet(request):\n    sheet = Sheet(owner=request.user)\n    sheet.save()\n    # need response redirect in order to reset url\n    return HttpResponseRedirect(reverse('sheet_page', kwargs={\n        'username': request.user.username,\n        'sheet_id': sheet.id\n    }))\n\n\n@fetch_users_or_public_sheet\ndef page(request, sheet):\n    profile = request.user.get_profile()\n    response = render(\n        request,\n        'sheet_page.html',\n        {\n            'sheet': sheet,\n            'user': request.user,\n            'profile': profile,\n            'import_form': ImportCSVForm()\n        }\n    )\n    if not profile.has_seen_sheet_page:\n        profile.has_seen_sheet_page = True\n        profile.save()\n\n        context = Context(dict(user=request.user))\n        email_text = get_template('welcome_email.txt').render(context)\n        send_mail(\n            'Welcome to Dirigible',\n            email_text,\n            '',\n            [request.user.email],\n            fail_silently=True\n        )\n    return response\n\n\n@login_required\n@fetch_users_sheet\ndef set_cell_formula(request, sheet):\n    column = int(request.POST[\"column\"])\n    row = int(request.POST[\"row\"])\n    formula = request.POST[\"formula\"]\n    worksheet = sheet.unjsonify_worksheet()\n    worksheet.set_cell_formula(column, row, formula)\n    sheet.jsonify_worksheet(worksheet)\n    if update_sheet_with_version_check(sheet, contents_json=sheet.contents_json):\n        response = 'OK'\n    else:\n        response = 'FAILED'\n    return HttpResponse(response)\n\n\n@login_required\n@fetch_users_sheet\ndef clear_cells(request, sheet):\n    positions = tuple(map(int, request.POST['range'].split(',')))\n    start_col, start_row, end_col, end_row = positions\n\n    worksheet = sheet.unjsonify_worksheet()\n    worksheet.cell_range((start_col, start_row), (end_col, end_row)).clear()\n    sheet.jsonify_worksheet(worksheet)\n\n    if update_sheet_with_version_check(sheet, contents_json=sheet.contents_json):\n        return HttpResponse('OK')\n    else:\n        return HttpResponse('FAILED')\n\n\n@login_required\n@fetch_users_sheet\ndef set_sheet_usercode(request, sheet):\n    usercode = request.POST['usercode'].replace('\\r\\n', '\\n')\n    if update_sheet_with_version_check(sheet, usercode=usercode):\n        return HttpResponse('OK')\n    else:\n        return HttpResponse('FAILED')\n\n\n@login_required\n@fetch_users_sheet\ndef set_sheet_name(request, sheet):\n    sheet.name = request.POST['new_value']\n    sheet.save()\n    return HttpResponse('{\"is_error\":false, \"html\":\"%s\"}' % (escape(sheet.name,)))\n\n\n@login_required\n@fetch_users_sheet\ndef set_column_widths(request, sheet):\n    sheet.column_widths.update(json.loads(request.POST['column_widths']))\n    sheet.save()\n    return HttpResponse('OK')\n\n\n@login_required\n@fetch_users_sheet\ndef set_sheet_security_settings(request, sheet):\n    sheet.api_key = request.POST['api_key']\n    sheet.allow_json_api_access = request.POST['allow_json_api_access'] == 'true'\n    sheet.is_public = request.POST['is_public'] == 'true'\n    sheet.save()\n    return HttpResponse('OK')\n\n\n@fetch_users_or_public_sheet\ndef get_json_grid_data_for_ui(request, sheet):\n    rnge = tuple(map(int, request.GET['range'].split(',')))\n    return HttpResponse(sheet_to_ui_json_grid_data(sheet.unjsonify_worksheet(), rnge))\n\n\n@fetch_users_or_public_sheet\ndef get_json_meta_data_for_ui(request, sheet):\n    return HttpResponse(sheet_to_ui_json_meta_data(sheet, sheet.unjsonify_worksheet()))\n\n\n@never_cache\n@login_required\n@fetch_users_sheet\n@rollback_on_exception\ndef calculate(request, sheet):\n    sheet.calculate()\n\n    transaction.commit()\n    sheet_in_db = Sheet.objects.get(pk=sheet.id)\n    sheet.merge_non_calc_attrs(sheet_in_db)\n\n    if update_sheet_with_version_check(sheet, contents_json=sheet.contents_json):\n        result = HttpResponse('OK')\n    else:\n        result = HttpResponse('{ \"message\": \"Recalc aborted: sheet changed\" }')\n    transaction.commit()\n    return result\n\n\n@login_required\n@fetch_users_sheet\ndef clipboard(request, sheet, action):\n    clipboard, clipboard_blank = Clipboard.objects.get_or_create(owner=request.user)\n    positions = tuple(map(int, request.POST['range'].split(',')))\n    start_col, start_row, end_col, end_row = positions\n\n    if action == 'copy':\n        clipboard.copy(sheet, (start_col, start_row), (end_col, end_row))\n        clipboard.save()\n        return HttpResponse('OK')\n\n    elif action == 'cut':\n        clipboard.cut(sheet, (start_col, start_row), (end_col, end_row))\n        if update_sheet_with_version_check(sheet, contents_json=sheet.contents_json):\n            clipboard.save()\n            return HttpResponse('OK')\n        else:\n            return HttpResponse('{ \"message\": \"Cut aborted: sheet changed\" }')\n\n    elif action == 'paste':\n        if clipboard_blank:\n            return HttpResponse('{ \"message\": \"Clipboard blank\" }')\n\n        clipboard.paste_to(sheet, (start_col, start_row), (end_col, end_row))\n        if update_sheet_with_version_check(sheet, contents_json=sheet.contents_json):\n            clipboard.save()\n            result = HttpResponse('OK')\n        else:\n            result = HttpResponse('{ \"message\": \"Paste aborted: sheet changed\" }')\n        return result\n\n"
  },
  {
    "path": "dirigible/sheet/views_api_0_1.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\nimport simplejson as json\nfrom datetime import datetime, timedelta\nfrom urllib2 import HTTPError\n\nfrom django.db import transaction\nfrom django.http import HttpResponse, HttpResponseForbidden\nfrom django.shortcuts import get_object_or_404\n\nfrom .cell import undefined\nfrom .models import Sheet\nfrom .utils.cell_name_utils import cell_ref_as_string_to_coordinates\nfrom .views import rollback_on_exception\nfrom user.models import OneTimePad\n\n\n@rollback_on_exception\ndef calculate_and_get_json_for_api(request, username, sheet_id):\n    sheet = get_object_or_404(Sheet, pk=sheet_id, owner__username=username)\n    pads = None\n\n    if request.method == 'POST':\n        params = request.POST\n    else:\n        params = request.GET\n\n    if 'api_key' in params:\n        if not sheet.allow_json_api_access:\n            transaction.rollback()\n            return HttpResponseForbidden()\n        elif params['api_key'] != sheet.api_key:\n            transaction.rollback()\n            return HttpResponseForbidden()\n    elif 'dirigible_l337_private_key' in params:\n        pads = OneTimePad.objects.filter(\n            user=sheet.owner,\n            guid=params['dirigible_l337_private_key']\n        )\n        too_old = datetime.now() - timedelta(36000)\n        if len(pads) != 1 or pads[0].creation_time < too_old:\n            transaction.rollback()\n            return HttpResponseForbidden()\n    else:\n        transaction.rollback()\n        return HttpResponseForbidden()\n\n    worksheet = sheet.unjsonify_worksheet()\n    for encoded_loc, new_formula in params.items():\n        colrow = cell_ref_as_string_to_coordinates(encoded_loc)\n        if colrow is not None:\n            col, row = colrow\n            worksheet.set_cell_formula(col, row, new_formula)\n    sheet.jsonify_worksheet(worksheet)\n\n    try:\n        sheet.calculate()\n        worksheet = sheet.unjsonify_worksheet()\n        if worksheet._usercode_error:\n            return HttpResponse(json.dumps({\n                \"usercode_error\": {\n                    \"message\": worksheet._usercode_error[\"message\"],\n                    \"line\": str(worksheet._usercode_error[\"line\"])\n                }\n            }))\n        response = HttpResponse(\n            _sheet_to_value_only_json(sheet.name, worksheet))\n        response['Access-Control-Allow-Origin'] = '*'\n        return response\n    except (Exception, HTTPError), e:\n        return HttpResponse(str(e))\n    finally:\n        transaction.commit()\n\n\ndef _sheet_to_value_only_json(sheet_name, worksheet):\n    result = {'name': sheet_name}\n    for (col, row), cell in worksheet.items():\n        col_dict = result.setdefault(col, {})\n        if cell.value is not undefined:\n            col_dict[row] = cell.value\n    return json.dumps(result, default=unicode)\n"
  },
  {
    "path": "dirigible/sheet/worksheet.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\nfrom cgi import escape\nimport csv\nimport json\nimport simplejson as json\nfrom StringIO import StringIO\nfrom threading import Lock\nfrom xlrd import error_text_from_code, xldate_as_tuple, XL_CELL_DATE, XL_CELL_ERROR\n\nfrom .cell import Cell, undefined\nfrom .cell_range import CellRange\nfrom .utils.cell_name_utils import (\n    cell_name_to_coordinates, column_name_to_index,\n    cell_range_as_string_to_coordinates\n)\n\n\n\nclass InvalidKeyError(Exception):\n    pass\n\n\n\nclass Bounds(tuple):\n\n    def __init__(self, (left, top, right, bottom)):\n        super(tuple, self).__init__((left, top, right, bottom))\n        self.left, self.top, self.right, self.bottom = left, top, right, bottom\n\n\ndef dump_cell_to_json_stream(stream, col, row, cell):\n    stream.write('\"%s,%s\": { ' % (col, row))\n    stream.write('\"formula\": %s, ' % (json.dumps(cell.formula),))\n    stream.write('\"formatted_value\": %s ' % (json.dumps(cell.formatted_value),))\n    if cell.python_formula:\n        stream.write(', \"python_formula\": %s ' % (json.dumps(cell.python_formula),))\n    if cell.dependencies:\n        stream.write(', \"dependencies\": %s ' % (\n            json.dumps(map(list, cell.dependencies)),)\n        )\n    if cell.error:\n        stream.write(', \"error\": %s ' % (json.dumps(cell.error),))\n    try:\n        stream.write(', \"value\": %s ' % (json.dumps(cell.value, allow_nan=False),))\n    except (TypeError, ValueError):\n        # Not JSONifiable\n        pass\n    stream.write('}')\n\n\ndef worksheet_to_json(worksheet):\n    stream = StringIO()\n    stream.write(\"{ \")\n\n    stream.write('\"_console_text\": %s, ' % (json.dumps(worksheet._console_text),))\n    stream.write('\"_usercode_error\": %s ' % (json.dumps(worksheet._usercode_error),))\n\n    for (col, row), cell in worksheet.iteritems():\n        stream.write(',')\n        dump_cell_to_json_stream(stream, col, row, cell)\n\n    stream.write(\" }\")\n    result = stream.getvalue()\n    stream.close()\n    return result\n\n\ndef worksheet_from_json(json_string):\n    #use json for read ops because of better performance\n    #keep simplejson for write ops as it's more robust\n    worksheet_dict = json.loads(json_string)\n    worksheet = Worksheet()\n    for (key, value) in worksheet_dict.iteritems():\n        if key == \"_console_text\":\n            worksheet._console_text = value\n        elif key == \"_usercode_error\":\n            worksheet._usercode_error = value\n        else:\n            col_str, row_str = key.split(\",\")\n            cell = Cell()\n            cell._formula = value[\"formula\"]\n            cell._python_formula = value.get(\"python_formula\")\n            cell.dependencies = map(tuple, value.get(\"dependencies\", []))\n            cell.error = value.get(\"error\")\n            cell._value = value.get(\"value\", undefined)\n            cell.formatted_value = value[\"formatted_value\"]\n            worksheet[int(col_str), int(row_str)] = cell\n    return worksheet\n\n\ndef worksheet_to_csv(worksheet, encoding):\n    stream = StringIO()\n    writer = csv.writer(stream)\n\n    if worksheet:\n        _, __, right, bottom = worksheet.bounds\n        for row in range(1, bottom + 1):\n            row_list = []\n            for col in range(1, right + 1):\n                value = worksheet[col, row].value\n                encoded_value = ''\n                if not value is undefined:\n                    if hasattr(value, 'encode'):\n                        encoded_value = value.encode(encoding)\n                    else:\n                        encoded_value = value\n                row_list.append(encoded_value)\n            writer.writerow(row_list)\n\n    result = stream.getvalue()\n    stream.close()\n    return result\n\n\ndef worksheet_from_excel(excel_sheet):\n    worksheet = Worksheet()\n    for col in range(excel_sheet.ncols):\n        for row in range(excel_sheet.nrows):\n            cell = excel_sheet.cell(row, col)\n            if cell.ctype == XL_CELL_ERROR:\n                formula = '=%s' % (error_text_from_code[cell.value], )\n            elif cell.ctype == XL_CELL_DATE:\n                formula = '=DateTime(%s, %s, %s, %s, %s, %s)' % xldate_as_tuple(\n                    cell.value, excel_sheet.book.datemode)\n            else:\n                formula = unicode(excel_sheet.cell(row, col).value)\n            worksheet[col + 1, row + 1].formula = formula\n    return worksheet\n\n\nclass Worksheet(dict):\n\n    def __init__(self):\n        self.name = None\n        self._console_text = ''\n        self._usercode_error = None\n        self._console_lock = Lock()\n\n\n    def __getitem__(self, key):\n        location = self.to_location(key)\n        if not location:\n            raise InvalidKeyError(\"%r is not a valid cell location\" % (key,))\n\n        return self.setdefault(location, Cell())\n\n\n    def __setitem__(self, key, item):\n        location = self.to_location(key)\n        if not location:\n            raise InvalidKeyError(\"%r is not a valid cell location\" % (key,))\n\n        if not isinstance(item, Cell):\n            raise TypeError(\"Worksheet locations must be Cell objects\")\n\n        dict.__setitem__(self, location, item)\n\n\n    def __getattr__(self, name):\n        location = self.to_location(name)\n        if not location:\n            raise AttributeError(\"'Worksheet' object has no attribute %r\" % (name,))\n\n        return self.__getitem__(name)\n\n\n    def __setattr__(self, name, value):\n        location = cell_name_to_coordinates(name)\n        if location:\n            self.__setitem__(location, value)\n        else:\n            super(Worksheet, self).__setattr__(name, value)\n\n\n    def __repr__(self):\n        if self.name:\n            return '<Worksheet %s>' % (self.name,)\n        else:\n            return '<Worksheet>'\n\n\n    def __eq__(self, other):\n        return (\n            isinstance(other, Worksheet) and\n            dict(self) == dict(other) and\n            self.name == other.name\n        )\n\n\n    def __ne__(self, other):\n        return not self.__eq__(other)\n\n\n    def to_location(self, key):\n        if isinstance(key, tuple) and len(key) == 2:\n            col, row = key\n            if isinstance(col, basestring):\n                col = column_name_to_index(col)\n            if isinstance(col, (int, long)) and isinstance(row, (int, long)):\n                return col, row\n            return None\n        elif isinstance(key, basestring):\n            return cell_name_to_coordinates(key)\n        return None\n\n\n    def add_console_text(self, error_text, log_type='error'):\n        self._console_lock.acquire()\n        self._console_text += '<span class=\"console_%s_text\">%s</span>' \\\n                        % (log_type, escape(error_text))\n        self._console_lock.release()\n\n\n    def set_cell_formula(self, col, row, formula):\n        if not formula:\n            if (col, row) in self:\n                del self[col, row]\n        else:\n            self[col, row].formula = formula\n\n\n    def clear_values(self):\n        to_delete = []\n        for location, cell in self.items():\n            if cell.formula or cell.python_formula:\n                cell.value = undefined\n                cell.error = None\n            else:\n                to_delete.append(location)\n        for location in to_delete:\n            del self[location]\n\n\n    #--methods intended for public user consumption--\n\n    def cell_range(self, start_or_string_cellrange, end=None):\n        if isinstance(start_or_string_cellrange, basestring) and end is None:\n            start_and_end = cell_range_as_string_to_coordinates(start_or_string_cellrange)\n            if start_and_end is None:\n                raise ValueError(\"Invalid cell range '%s'\" % (start_or_string_cellrange, ))\n            return CellRange(self, *start_and_end)\n\n        def convert_if_needed(cell_ref):\n            if isinstance(cell_ref, basestring):\n                return cell_name_to_coordinates(cell_ref)\n            else:\n                return cell_ref\n        start_tuple = convert_if_needed(start_or_string_cellrange)\n        end_tuple = convert_if_needed(end)\n        if start_tuple is None:\n            if end_tuple is None:\n                raise ValueError('Neither %s nor %s are valid cell locations' % (start_or_string_cellrange, end))\n            raise ValueError('%s is not a valid cell location' % (start_or_string_cellrange, ))\n        if end_tuple is None:\n            raise ValueError('%s is not a valid cell location' % (end, ))\n        return CellRange(self, start_tuple, end_tuple)\n\n\n    @property\n    def bounds(self):\n        if not self:\n            return None\n        locations = self.iterkeys()\n        col, row = locations.next()\n        left = right = col\n        top = bottom = row\n        for col, row in locations:\n            if col < left:\n                left = col\n            if row < top:\n                top = row\n            if col > right:\n                right = col\n            if row > bottom:\n                bottom = row\n        return Bounds((left, top, right, bottom))\n"
  },
  {
    "path": "dirigible/user/__init__.py",
    "content": ""
  },
  {
    "path": "dirigible/user/admin.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\r\n# See LICENSE.md\r\n#\r\n\r\nfrom django.contrib import admin\r\nfrom django.contrib.auth.admin import UserAdmin\r\n\r\nfrom user.models import OneTimePad, User, UserProfile\r\n\r\n\r\nadmin.site.register(OneTimePad)\r\n\r\nadmin.site.unregister(User)\r\n\r\nclass UserProfileInline(admin.StackedInline):\r\n    model = UserProfile\r\n\r\ndef has_seen_sheet_page(user):\r\n    return user.get_profile().has_seen_sheet_page\r\n\r\nhas_seen_sheet_page.boolean = True\r\n\r\nclass MyUserAdmin(UserAdmin):\r\n    inlines = [UserProfileInline]\r\n    list_display = ('username', 'email', 'is_staff', 'is_active', 'date_joined', 'last_login', has_seen_sheet_page)\r\n\r\nadmin.site.register(User, MyUserAdmin)\r\n\r\n"
  },
  {
    "path": "dirigible/user/forms.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\r\n# See LICENSE.md\r\n#\r\n\r\nfrom registration.forms import RegistrationForm\r\n\r\n\r\nclass DirigibleRegistrationForm(RegistrationForm):\r\n    def __init__(self, *args, **kwargs):\r\n        super(RegistrationForm, self).__init__(*args,  **kwargs)\r\n        self.fields['username'].error_messages['required'] = 'Please enter a username.'\r\n        self.fields['email'].error_messages['required'] = 'Please enter your email address.'\r\n        self.fields['email'].error_messages['invalid'] = 'Please enter a valid email address.'\r\n        self.fields['password1'].error_messages['required'] = 'Please enter a password.'\r\n        self.fields['password2'].error_messages['required'] = 'Please enter a password.'\r\n"
  },
  {
    "path": "dirigible/user/migrations/0001_initial.py",
    "content": "# -*- coding: utf-8 -*-\nfrom __future__ import unicode_literals\n\nfrom django.db import models, migrations\nfrom django.conf import settings\nimport user.models\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('auth', '0001_initial'),\n        migrations.swappable_dependency(settings.AUTH_USER_MODEL),\n    ]\n\n    operations = [\n        migrations.CreateModel(\n            name='OneTimePad',\n            fields=[\n                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),\n                ('creation_time', models.DateTimeField(auto_now_add=True)),\n                ('guid', models.CharField(default=user.models.get_uid, max_length=72)),\n            ],\n            options={\n            },\n            bases=(models.Model,),\n        ),\n        migrations.CreateModel(\n            name='UserProfile',\n            fields=[\n                ('user', models.OneToOneField(primary_key=True, serialize=False, to=settings.AUTH_USER_MODEL)),\n                ('has_seen_sheet_page', models.BooleanField(default=False)),\n            ],\n            options={\n            },\n            bases=(models.Model,),\n        ),\n        migrations.AddField(\n            model_name='onetimepad',\n            name='user',\n            field=models.ForeignKey(to=settings.AUTH_USER_MODEL),\n            preserve_default=True,\n        ),\n    ]\n"
  },
  {
    "path": "dirigible/user/migrations/__init__.py",
    "content": ""
  },
  {
    "path": "dirigible/user/models.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\nfrom uuid import uuid4\n\nfrom django.contrib.auth.models import AnonymousUser, User\nfrom django.db import models\n\ndef get_uid():\n    return uuid4()\n\nclass OneTimePad(models.Model):\n    creation_time = models.DateTimeField(auto_now_add=True)\n    user = models.ForeignKey(User)\n    guid = models.CharField(default=get_uid, max_length=72)\n\n\nclass UserProfile(models.Model):\n    user = models.OneToOneField(User, primary_key=True)\n    has_seen_sheet_page = models.BooleanField(default=False)\n\nAnonymousUser.email = None\n\nclass AnonymousProfile(object):\n    has_seen_sheet_page = True\n    save = lambda _: None\n\nUser.get_profile = lambda u: UserProfile.objects.get_or_create(user=u)[0]\nAnonymousUser.get_profile = lambda u: AnonymousProfile()\n"
  },
  {
    "path": "dirigible/user/signup_urls.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\nfrom django.conf.urls import *\nfrom registration.views import activate\n\nfrom user.views import register, registration_complete\n\n\nurlpatterns = patterns('',\n\n    url(\n        r'^register/$',\n        register,\n        name=\"registration_register\"\n    ),\n\n    url(\n        r'^activate/(?P<activation_key>\\w+)/$',\n        activate,\n        name='registration_activate'\n    ),\n\n    url(\n        r'^register/complete/$',\n        registration_complete,\n        name='registration_complete'\n    ),\n\n)\n"
  },
  {
    "path": "dirigible/user/templates/login.html",
    "content": "{% extends \"non_sheet_page_small_logo.html\" %}\n\n{% block title %}\n    Login: Dirigible\n{% endblock %}\n\n{% block head %}\n    {{ block.super }}\n    <link rel=\"stylesheet\" href=\"/static/dirigible/styles/login.css\" type=\"text/css\" media=\"screen\" charset=\"utf-8\" />\n{% endblock %}\n\n\n{% block content %}\n\n    <div class=\"clear\"> </div>\n\n        <div id=\"id_login_form_wrap\" class=\"grey_rounded_corner_box\">\n\n            <div id=\"id_login_form_header\">\n                <h1>Log in</h1>\n\n                <p id=\"id_login_signup_blurb\">\n                    Don't have a Dirigible account?\n                    {% if next %}\n                        <a href=\"{% url 'registration_register' %}?next={{ next }}\" id=\"id_login_signup_link\">Click here to sign up</a>.\n                    {% else %}\n                        <a href=\"{% url 'registration_register' %}\" id=\"id_login_signup_link\">Click here to sign up</a>.\n                    {% endif %}\n                </p>\n            </div>\n\n            <div class=\"clear\"></div>\n\n            {% if form.errors %}\n            <p id=\"id_login_error\">The user name or password is incorrect. Please try again.</p>\n            {% endif %}\n            <div id=\"id_login_form\">\n\n                <form action=\"{% url 'login' %}\" method=\"post\">\n                    {% csrf_token %}\n\n                    <div id=\"id_username_wrap\">\n                        <span id=\"id_username_label\">{{ form.username.label_tag }}:</span>\n                        {{ form.username }}\n                    </div>\n                    <div id=\"id_password_wrap\">\n                        <span id=\"id_password_label\">{{ form.password.label_tag }}:</span>\n                        {{ form.password }}\n                    </div>\n\n                    <input type=\"hidden\" name=\"next\" value=\"{{ next }}\" />\n\n                    <div class=\"clear\"> </div>\n\n                    <input type=\"submit\" class=\"blue_button\" id=\"id_login\" value=\"Login\" />\n\n                </form>\n\n                <div class=\"clear\"> </div>\n\n            </div>\n        </div>\n\n    <script type=\"text/javascript\">\n        document.getElementById('id_username').focus();\n    </script>\n{% endblock %}\n"
  },
  {
    "path": "dirigible/user/templates/registration/activate.html",
    "content": "{% extends \"non_sheet_page_small_logo.html\" %}\r\n\r\n{% block title %}\r\n    Welcome to Project Dirigible\r\n{% endblock %}\r\n\r\n{% block head %}\r\n    {{ block.super }}\r\n    <link rel=\"stylesheet\" href=\"/static/dirigible/styles/registration.css\" type=\"text/css\" media=\"screen\" charset=\"utf-8\" />\r\n    <link rel=\"stylesheet\" href=\"/static/dirigible/styles/login.css\" type=\"text/css\" media=\"screen\" charset=\"utf-8\" />\r\n{% endblock %}\r\n\r\n{% block content %}\r\n    <div class=\"clear\"> </div>\r\n\r\n    <div id=\"id_signup_vwrap\" class=\"centered-block\">\r\n        <div id=\"id_signup_hwrap\" class=\"grey_rounded_corner_box\">\r\n            {% if account %}\r\n                <h3>Welcome to Dirigible!</h3>\r\n\r\n                <p>\r\n                    Thank you for signing up, you're ready to go.  Just log in below\r\n                    using the username and password you registered with.\r\n                </p>\r\n\r\n                <div id=\"id_login_form\">\r\n                    <form action=\"{% url login %}\" method=\"post\">\r\n                        {% csrf_token %}\r\n\r\n                        <div id=\"id_username_wrap\">\r\n                            <span id=\"id_username_label\"><label for=\"id_username\">Username</label>:</span>\r\n\r\n                            <input id=\"id_username\" type=\"text\" name=\"username\" maxlength=\"30\" />\r\n                        </div>\r\n                        <div id=\"id_password_wrap\">\r\n                            <span id=\"id_password_label\"><label for=\"id_password\">Password</label>:</span>\r\n                            <input type=\"password\" name=\"password\" id=\"id_password\" />\r\n                        </div>\r\n\r\n                        <input type=\"hidden\" name=\"next\" value=\"\" />\r\n\r\n                        <div class=\"clear\"> </div>\r\n\r\n                        <input type=\"submit\" class=\"blue_button\" id=\"id_login\" value=\"Login\" />\r\n\r\n                    </form>\r\n                </div>\r\n            {% else %}\r\n                <h3>Unrecognised activation code</h3>\r\n\r\n                <p>\r\n                    Sorry, the activation link you used was not recognised, or has already been used.\r\n                    Please check it and try again; if you think your account might already be activated,\r\n                    try <a href=\"{% url login %}\">logging in</a>.\r\n                </p>\r\n\r\n            {% endif %}\r\n        </div>\r\n\r\n    </div>\r\n{% endblock %}\r\n\r\n"
  },
  {
    "path": "dirigible/user/templates/registration/activation_email.txt",
    "content": "Hi,\r\n\r\nThank you for signing up for Dirigible!\r\n\r\nClick on the following link to start using it:\r\n\r\n<http://localhost:8000{% url registration_activate activation_key %}>\r\n\r\nIf you cannot click the link from your email program, please copy the URL and paste it into your web browser.\r\n\r\nIf you do not want to use Dirigible, simply ignore this message and we'll not bother you again.\r\n\r\n\r\nBest Regards,\r\n"
  },
  {
    "path": "dirigible/user/templates/registration/activation_email_subject.txt",
    "content": "Dirigible Beta Sign-up"
  },
  {
    "path": "dirigible/user/templates/registration/registration_complete.html",
    "content": "{% extends \"non_sheet_page_small_logo.html\" %}\r\n\r\n{% block title %}\r\n    Project Dirigible Beta Sign-up\r\n{% endblock %}\r\n\r\n{% block head %}\r\n    {{ block.super }}\r\n    <link rel=\"stylesheet\" href=\"/static/dirigible/styles/registration.css\" type=\"text/css\" media=\"screen\" charset=\"utf-8\" />\r\n{% endblock %}\r\n\r\n{% block content %}\r\n    <div class=\"clear\"> </div>\r\n\r\n    <div id=\"id_signup_vwrap\" class=\"centered-block\">\r\n        <div id=\"id_signup_hwrap\" class=\"grey_rounded_corner_box\">\r\n            <h3>Thank you!</h3>\r\n\r\n            <p>\r\n                We have sent a confirmation email to\r\n                {% if email_address %}\r\n                    <b>{{ email_address }}</b>\r\n                {% else %}\r\n                    the email address that you provided\r\n                {% endif %}\r\n            </p>\r\n            <p>\r\n                Please click the link in the email to complete the sign-up.\r\n            </p>\r\n\r\n            <p id=\"id_link_home_para\"><a id=\"id_link_home\" href=\"/\">Return to Dirigible home</a>\r\n        </div>\r\n    </div>\r\n{% endblock %}\r\n\r\n"
  },
  {
    "path": "dirigible/user/templates/registration/registration_form.html",
    "content": "{% extends \"non_sheet_page_small_logo.html\" %}\n\n{% block title %}\n    Project Dirigible Beta Sign-up\n{% endblock %}\n\n{% block head %}\n    {{ block.super }}\n    <link rel=\"stylesheet\" href=\"/static/dirigible/styles/registration.css\" type=\"text/css\" media=\"screen\" charset=\"utf-8\" />\n\n    <script type=\"text/javascript\" src=\"/static/jquery/jquery-1.4.2.min.js\"></script>\n\n    <script type=\"text/javascript\">\n        $(function() {\n            var checkInProgress = false;\n            function updatePasswordErrorState() {\n                if (checkInProgress) {\n                    return;\n                }\n                checkInProgress = true;\n                if ($('#id_password1').val() == $('#id_password2').val()) {\n                    $('#id_password1').removeAttr('class');\n                    $('#id_password2').removeAttr('class');\n                } else {\n                    $('#id_password1').attr('class', 'error');\n                    $('#id_password2').attr('class', 'error');\n                }\n                checkInProgress = false;\n            }\n\n            $('#id_password1').bind(\"input\", updatePasswordErrorState);\n            $('#id_password2').bind(\"input\", updatePasswordErrorState);\n            $('#id_password1').bind(\"propertychange\", updatePasswordErrorState);\n            $('#id_password2').bind(\"propertychange\", updatePasswordErrorState);\n        });\n    </script>\n\n{% endblock %}\n\n{% block content %}\n    <div class=\"clear\"> </div>\n\n    <div id=\"id_signup_vwrap\" class=\"centered-block\">\n        <div id=\"id_signup_hwrap\" class=\"grey_rounded_corner_box\">\n\n<!--[if IE]>\n<div id=\"id_ie_warning\"><span><em>It looks like you're using Internet Explorer.\nWe're sorry to say we're currently working on a couple of bugs in IE.\nIf you can use\n<a href=\"http://www.browserchoice.eu/BrowserChoice/browserchoice_en.htm\">another browser</a>,\nplease do!</span></em></div>\n<![endif]-->\n\n            <form action=\"{% url registration_register %}\" method=\"POST\">\n                {% csrf_token %}\n                {% if next %}\n                <input type=\"hidden\" name=\"next\" value=\"{{ next }}\" />\n                {% endif %}\n                <table id=\"id_signup_table\">\n                    {% for field in form %}\n                        {% if field.errors %}\n                            <tr>\n                                <td class=\"label\"></td>\n                                <td id=\"id_{{field.name}}_error\" class=\"input\">{{field.errors}}</td>\n                            </tr>\n                        {% endif %}\n                        <tr>\n                            <td class=\"label\">{{field.label|capfirst}}:</td>\n                            <td class=\"input\">{{field}}</td>\n                        </tr>\n                    {% endfor %}\n\n                    {% if form.non_field_errors %}\n                    <tr id=\"id_non_field_errors\">\n                        <td colspan=\"2\">{{ form.non_field_errors }}</td>\n                    </tr>\n                    {% endif %}\n\n                    <tr>\n                        <td colspan=\"2\" id=\"id_signup_button_cell\"><input id=\"id_signup_button\" type=\"submit\" class=\"blue_button\" value=\"Sign up\" /></td>\n                    </tr>\n                </table>\n            </form>\n\n        </div>\n        <div id=\"id_we_dont_spam\">\n            We promise not to spam or pass your details on to anyone else.\n        </div>\n    </div>\n{% endblock %}\n\n"
  },
  {
    "path": "dirigible/user/templates/user_page.html",
    "content": "{% extends \"non_sheet_page_small_logo.html\" %}\n\n{% block title %}\n    {{ user.username }}'s Dashboard: Dirigible\n{% endblock %}\n\n{% block head %}\n    {{ block.super }}\n    <link rel=\"stylesheet\" href=\"/static/dirigible/styles/user_page.css\" type=\"text/css\" media=\"screen\" charset=\"utf-8\" />\n{% endblock %}\n\n\n{% block content %}\n\n    <script type=\"text/javascript\">\n        $(function() {\n            function show_form() {\n                $('#id_change_password_error').html('');\n                $('#id_change_password_form input[type=\"password\"]').val('');\n                $('#id_change_password_form').show('blind');\n                $('#id_change_password_button').hide('blind');\n            }\n            function show_button() {\n                $('#id_change_password_form').hide('blind');\n                $('#id_change_password_button').show('blind');\n            }\n            $('#id_change_password_button').click(function() {\n                show_form();\n                return false;\n            });\n            $('#id_change_password_cancel_button').click(function() {\n                show_button();\n                return false;\n            });\n            $('#id_change_password_form').submit(function() {\n                $.post(\n                    '{% url 'change_password' user.username %}',\n                    $('#id_change_password_form').serialize(),\n                    function(data) {\n                        if (data == 'Your password has been changed.') {\n                            show_button();\n                            $('#id_change_password_success').html(data).show();\n                            setTimeout(\" $('#id_change_password_success').hide('fade')\", 3000);\n                        } else {\n                            $('#id_change_password_error').html(data);\n                        }\n                    }\n                );\n                return false;     \n            });\n        });\n    </script>\n\n    <div class=\"clear\"></div>\n\n    <div id='id_dashboard_wrap' class=\"centered-block\">\n\n        <div id=\"id_sheets\" class=\"grey_rounded_corner_box\">\n<!--[if IE]>\n<div id=\"id_ie_warning\"><span><em>It looks like you're using Internet Explorer.\nWe're sorry to say we're currently working on a couple of bugs in IE.\nIf you can use \n<a href=\"http://www.browserchoice.eu/BrowserChoice/browserchoice_en.htm\">another browser</a>,\nplease do!</span></em></div>\n<![endif]-->\n\n\n            <div id=\"id_sheets_header\">\n                <h2 id=\"id_sheets_title\" class=\"section-title\">Sheets</h2>\n\n                <a id=\"id_create_new_sheet\" href=\"{% url 'new_sheet' %}\">Create new sheet...</a>\n            </div>\n\n            <div id=\"id_sheets_body\">\n                {% if sheets %}\n                    <ul id=\"id_sheets_list\">\n                        {% for s in sheets %}\n                            <li><a href=\"{% url 'sheet_page' user.username s.id %}\">{{s.name}}</a></li>\n                        {% endfor %}\n                    </ul>\n                {% else %}\n                    <p id=\"id_no_sheets_message\">You have no sheets. Create one to get started!</p>\n                {% endif %}\n            </div>\n\n        </div>\n\n        <div id=\"id_right_column\">\n            <div id=\"id_account_details\">\n                <h2 id=\"id_account_details_title\" class=\"section-title\">Account details</h2>\n\n                <a id=\"id_change_password_button\"  href=\"#\">Change password...</a>\n                <form id=\"id_change_password_form\" method=\"POST\">\n                    {% csrf_token %}\n                    Current Password: <input id=\"id_change_password_current_password\" name=\"id_change_password_current_password\" type=\"password\"><br />\n                    New Password: <input id=\"id_change_password_new_password\" name=\"id_change_password_new_password\" type=\"password\"><br />\n                    Confirm Password: <input id=\"id_change_password_new_password_again\" name=\"id_change_password_new_password_again\" type=\"password\"><br />\n                    <div id=\"id_change_password_buttons\">\n                        <input type=\"submit\" class=\"blue_button\" id=\"id_change_password_ok_button\" type=\"button\" value=\"OK\">\n                        <input id=\"id_change_password_cancel_button\" class=\"blue_button\" type=\"button\" value=\"Cancel\">\n                    </div>\n                    <div class=\"clear\"></div>\n                    <div id=\"id_change_password_error\"></div>\n                </form>\n                <div id=\"id_change_password_success\"></div>\n\n            </div>\n\n            <div id=\"id_example_sheets\">\n            <h2 class=\"section-title\">Example sheets</h2>\n            Here are some <a href=\"{% url 'featured_sheets' %}\">example spreadsheets</a> you can copy into your account to\n            get you started.\n            </div>\n        </div>\n\n    </div>\n\n    <div class=\"clear\"></div>\n\n{% endblock %}\n"
  },
  {
    "path": "dirigible/user/templates/welcome_email.txt",
    "content": "Dear {{user.username}}\r\n\r\nWelcome to Dirigible!\r\n\r\n"
  },
  {
    "path": "dirigible/user/tests/__init__.py",
    "content": "\n"
  },
  {
    "path": "dirigible/user/tests/test_forms.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\r\n# See LICENSE.md\r\n#\r\n\r\nfrom registration.forms import RegistrationForm\r\n\r\nfrom dirigible.test_utils import ResolverTestCase\r\nfrom user.forms import DirigibleRegistrationForm\r\n\r\n\r\nclass DirigibleRegistrationFormTest(ResolverTestCase):\r\n\r\n    def test_is_registration_form(self):\r\n        form = DirigibleRegistrationForm()\r\n        self.assertTrue(isinstance(form, RegistrationForm))\r\n\r\n\r\n    def test_error_messages(self):\r\n        form = DirigibleRegistrationForm()\r\n        self.assertEquals(form.fields['username'].error_messages['required'], \"Please enter a username.\")\r\n        self.assertEquals(form.fields['email'].error_messages['required'], \"Please enter your email address.\")\r\n        self.assertEquals(form.fields['email'].error_messages['invalid'], \"Please enter a valid email address.\")\r\n        self.assertEquals(form.fields['password1'].error_messages['required'], \"Please enter a password.\")\r\n        self.assertEquals(form.fields['password2'].error_messages['required'], \"Please enter a password.\")\r\n"
  },
  {
    "path": "dirigible/user/tests/test_models.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\nfrom mock import patch\nfrom django.utils import timezone\n\nfrom django.contrib.auth.models import AnonymousUser, User\n\nfrom dirigible.test_utils import ResolverTestCase\nfrom user.models import AnonymousProfile, OneTimePad, UserProfile\n\n\nclass TestOneTimePads(ResolverTestCase):\n\n    @patch('user.models.uuid4')\n    def test_OneTimePad_init(self, mock_uuid4):\n        user = User(username='Alice, traditionally')\n        user.save()\n        otp = OneTimePad(user=user)\n        otp.save()\n        otp = OneTimePad.objects.get(pk=otp.id)\n        self.assertTrue((timezone.now() - otp.creation_time).seconds < 1)\n        self.assertEquals(otp.user, user)\n        self.assertEquals(otp.guid, unicode(mock_uuid4.return_value))\n\n\n\nclass TestUserProfiles(ResolverTestCase):\n\n    def test_defaults(self):\n        user_profile = UserProfile()\n        self.assertEquals(user_profile.has_seen_sheet_page, False)\n\n\n    def test_save_new_user_creates_user_profile(self):\n        user = User(username='Kenny Ken')\n        user.save()\n        self.assertEquals(type(user.get_profile()), UserProfile)\n        self.assertEquals(user.get_profile().user, user)\n\n\nclass TestAnonymousUser(ResolverTestCase):\n\n    def test_anonymous_user_has_a_profile(self):\n        profile = AnonymousUser().get_profile()\n        self.assertEquals(type(profile), AnonymousProfile)\n\n    def test_anonymous_user_attrs(self):\n        self.assertIsNone(AnonymousUser().email)\n\n    def test_anonymous_profile_attrs(self):\n        profile = AnonymousUser().get_profile()\n        self.assertTrue(profile.has_seen_sheet_page)\n        self.assertIsNone(profile.save())\n\n"
  },
  {
    "path": "dirigible/user/tests/test_views.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\nfrom urlparse import urlparse\n\nfrom mock import Mock, patch, sentinel\n\nimport django\nfrom django.conf import settings\nfrom django.contrib.auth.models import AnonymousUser, User\nfrom django.core.urlresolvers import reverse\nfrom django.http import Http404, HttpRequest, HttpResponse, HttpResponseRedirect\n\nfrom dirigible.test_utils import assert_security_classes_exist, ResolverTestCase\n\nfrom user.views import (\n    change_password, copy_sheet_for_new_user_callback, redirect_to_front_page,\n    register, registration_complete, user_dashboard\n)\nfrom sheet.models import Sheet\n\n\ndef set_up_view_test(self):\n    self.user = User(username='userviewtestuser')\n    self.user.set_password('p455w0rd')\n    self.user.save()\n    self.request = HttpRequest()\n    self.request.user = self.user\n    self.request.META['SERVER_NAME'] = 'servername'\n\n\nclass RedirectToFrontPageTest(django.test.TestCase):\n\n    setUp = set_up_view_test\n\n    def test_redirects_to_user_page_when_logged_in(self):\n        response = redirect_to_front_page(self.request)\n\n        self.assertTrue(isinstance(response, HttpResponseRedirect))\n\n        redirect_url = urlparse(response['Location'])\n        self.assertEquals(redirect_url.path, reverse('front_page'))\n\n\nclass UserDashboardSecurityTest(django.test.TestCase):\n\n    setUp = set_up_view_test\n\n    def test_view_login_required(self):\n        request = HttpRequest()\n        request.user = AnonymousUser()\n        request.META['SERVER_NAME'] = 'servername'\n        request.META['SERVER_PORT'] = '80'\n        actual = user_dashboard(request)\n\n        self.assertTrue(isinstance(actual, HttpResponseRedirect))\n\n        redirect_url = urlparse(actual['Location'])\n        self.assertEquals(redirect_url.path, settings.LOGIN_URL)\n\n\nclass UserDashboardTest(django.test.TestCase):\n\n    setUp = set_up_view_test\n\n    def test_page_should_return_response_for_logged_in_owner(self):\n        actual = user_dashboard(self.request)\n        self.assertTrue(isinstance(actual, HttpResponse))\n\n    @patch('user.views.render_to_response')\n    def test_userpage_has_list_of_sheets(self, mock_render_to_response):\n        sheets = [\n            Sheet(owner=self.user),\n            Sheet(owner=self.user),\n            Sheet(owner=self.user),\n        ]\n        map(lambda x: x.save(), sheets)\n\n        user_dashboard(self.request)\n\n        objects_passed_to_template = mock_render_to_response.call_args[0][1]\n        self.assertTrue('sheets' in objects_passed_to_template)\n        passed_sheet_ids = [s.id for s in objects_passed_to_template['sheets']]\n        self.assertEquals(set(passed_sheet_ids), set(s.id for s in sheets))\n\n\n\nclass ChangePasswordSecurityTest(django.test.TestCase):\n\n    setUp = set_up_view_test\n\n    def test_view_should_raise_on_wrong_user(self):\n        wrong_user = User(username='validbutnotowner')\n        wrong_user.save()\n        self.assertRaises(Http404, change_password, self.request, wrong_user.username)\n\n\n    def test_view_404_errors_should_be_indistinguishable(self):\n        # We don't want users to be able to detect whether other users with particular names\n        # exist, so 404 from nonexistent users' pages should be indistinguishable from 404\n        # from trying to access existing users' pages.\n        e1 = None\n        try:\n            change_password(self.request, \"nonexistent_user\")\n        except Http404 as e1:\n            pass\n\n        existing_user = User(username='existing_user')\n        existing_user.save()\n        e2 = None\n        try:\n            change_password(self.request, existing_user.username)\n        except Http404 as e2:\n            pass\n\n        self.assertEquals(str(e1), str(e2))\n\n\n    def test_view_login_required(self):\n        request = HttpRequest()\n        request.META['SERVER_NAME'] = 'servername'\n        request.META['SERVER_PORT'] = '80'\n        request.user = AnonymousUser()\n        actual = change_password(request, self.user.username)\n\n        self.assertTrue(isinstance(actual, HttpResponseRedirect))\n\n        redirect_url = urlparse(actual['Location'])\n        self.assertEquals(redirect_url.path, settings.LOGIN_URL)\n\n\n    def test_view_should_raise_if_nonexistent_user(self):\n        self.assertRaises(Http404, change_password, self.request, 'baduser')\n\n\nclass ChangePasswordTest(django.test.TestCase):\n\n    setUp = set_up_view_test\n\n    def test_change_password_with_empty_fields(self):\n        actual = change_password(self.request, self.user.username)\n\n        self.assertTrue(isinstance(actual, HttpResponse))\n        self.assertEquals(actual.content, 'Current password incorrect.')\n\n\n    def test_change_password_with_wrong_old_password(self):\n        self.request.POST['id_change_password_current_password'] = 'wrongpassword'\n        self.request.POST['id_change_password_new_password'] = '1234'\n        self.request.POST['id_change_password_new_password_again'] = '1234'\n\n        actual = change_password(self.request, self.user.username)\n\n        self.assertTrue(isinstance(actual, HttpResponse))\n        self.assertEquals(actual.content, 'Current password incorrect.')\n\n\n    def test_change_password_with_differing_new_passwords(self):\n        self.request.POST['id_change_password_current_password'] = 'p455w0rd'\n        self.request.POST['id_change_password_new_password'] = '1234'\n        self.request.POST['id_change_password_new_password_again'] = '12345'\n\n        actual = change_password(self.request, self.user.username)\n\n        self.assertTrue(isinstance(actual, HttpResponse))\n        self.assertEquals(actual.content, 'Please provide the new password twice for confirmation.')\n\n\n    def test_change_password_does(self):\n        self.request.POST['id_change_password_current_password'] = 'p455w0rd'\n        self.request.POST['id_change_password_new_password'] = '1234'\n        self.request.POST['id_change_password_new_password_again'] = '1234'\n\n        actual = change_password(self.request, self.user.username)\n\n        self.assertTrue(isinstance(actual, HttpResponse))\n        self.assertEquals(actual.content, 'Your password has been changed.')\n\n        updated_user = User.objects.get(pk=self.user.id)\n        self.assertTrue(updated_user.check_password('1234'))\n\n\nclass RegistrationViewsTest(ResolverTestCase):\n\n    @patch('user.views.DirigibleRegistrationForm')\n    @patch('user.views.django_registration')\n    def test_register_puts_email_address_into_session_if_form_is_valid(self, mock_registration_module, mock_form_class):\n        \"\"\"\n        There seems to be no way of getting the user's details from the\n        registration confirmation page in the default django-registration\n        system.  So we wrap it with our own code to stuff the important\n        bits into the session.\n        \"\"\"\n        mock_form = mock_form_class.return_value\n        mock_form.is_valid.return_value = True\n        mock_form.cleaned_data = {'email': sentinel.email}\n\n        mock_request = Mock()\n        mock_request.session = {}\n        mock_request.POST = []\n        mock_request.GET = {}\n\n        register(mock_request)\n\n        self.assertEquals(mock_request.session['email-address'], sentinel.email)\n\n\n    @patch('user.views.copy_sheet_to_user')\n    def test_copy_sheet_for_new_user_callback_copies_sheet_specified_in_next_url(self, mock_copy_sheet_to_user):\n        from_user = User(username='from_user')\n        from_user.save()\n\n        to_user = User(username='to_user')\n        to_user.save()\n\n        from_sheet = Sheet(owner=from_user)\n        from_sheet.save()\n        next_url = '/user/admin/sheet/%s/copy_sheet/' % (from_sheet.id,)\n\n        callback = copy_sheet_for_new_user_callback(next_url)\n        callback(to_user)\n\n        self.assertCalledOnce(mock_copy_sheet_to_user, from_sheet, to_user)\n\n\n    @patch('user.views.DirigibleRegistrationForm')\n    @patch('user.views.django_registration')\n    def test_register_doesnt_put_email_address_into_session_if_form_is_not_valid(self, mock_registration_module, mock_form_class):\n        \"\"\"\n        There seems to be no way of getting the user's details from the\n        registration confirmation page in the default django-registration\n        system.  So we wrap it with our own code to stuff the important\n        bits into the session.\n        \"\"\"\n        mock_form = mock_form_class.return_value\n        mock_form.is_valid.return_value = False\n\n        mock_request = Mock()\n        mock_request.session = {}\n        mock_request.POST = []\n        mock_request.GET = {}\n\n        register(mock_request)\n\n        self.assertTrue('email-address' not in mock_request.session)\n\n\n    @patch('user.views.DirigibleRegistrationForm')\n    @patch('user.views.django_registration')\n    @patch('user.views.copy_sheet_for_new_user_callback')\n    def test_register_delegates_to_django_registration_with_form(self, mock_copy_sheet_callback, mock_registration_module, mock_form_class):\n        mock_form = mock_form_class.return_value\n        mock_form.is_valid.return_value = False\n\n        mock_request = Mock()\n        mock_request.POST = {}\n        mock_request.POST['next'] = 'next_page'\n\n        mock_callback = Mock()\n        mock_copy_sheet_callback.return_value = mock_callback\n\n        response = register(mock_request)\n\n        self.assertCalledOnce(mock_copy_sheet_callback, 'next_page')\n\n        self.assertCalledOnce(\n            mock_registration_module.views.register,\n            mock_request,\n            profile_callback=mock_callback,\n            form_class=mock_form_class\n        )\n        self.assertEquals(\n            response,\n            mock_registration_module.views.register.return_value\n        )\n\n\n    @patch('user.views.DirigibleRegistrationForm')\n    @patch('user.views.django_registration')\n    def test_register_provides_extra_context_only_if_its_provided(self, mock_registration_module, mock_form_class):\n        mock_form = mock_form_class.return_value\n        mock_form.is_valid.return_value = False\n\n        mock_request = Mock()\n        mock_request.POST = {}\n        mock_request.GET = {}\n\n        register(mock_request)\n\n        self.assertCalledOnce(\n            mock_registration_module.views.register,\n            mock_request,\n            form_class=mock_form_class\n        )\n\n        mock_registration_module.views.register.reset_mock()\n        mock_request.GET['next'] = 'next_page'\n\n        register(mock_request)\n\n        self.assertCalledOnce(\n            mock_registration_module.views.register,\n            mock_request,\n            form_class=mock_form_class,\n            extra_context={'next': 'next_page'}\n        )\n\n\n\n    @patch('user.views.render')\n    def test_registration_complete_renders_template_with_email_address_from_session(self, mock_render):\n        \"\"\"\n        There seems to be no way of getting the user's details from the\n        registration confirmation page in the default django-registration\n        system.  So we wrap it with our own code to stuff the important\n        bits into the session.\n        \"\"\"\n        mock_request = Mock()\n        mock_request.session = {'email-address': sentinel.email_address}\n        response = registration_complete(mock_request)\n        self.assertCalledOnce(\n            mock_render,\n            mock_request,\n            'registration/registration_complete.html',\n            {'email_address': sentinel.email_address}\n        )\n        self.assertEquals(response, mock_render.return_value)\n\n\n\n    @patch('user.views.render')\n    def test_registration_complete_renders_template_with_blank_email_address_if_none_in_session(self, mock_render):\n        \"\"\"\n        There seems to be no way of getting the user's details from the\n        registration confirmation page in the default django-registration\n        system.  So we wrap it with our own code to stuff the important\n        bits into the session.\n        \"\"\"\n        mock_request = Mock()\n        mock_request.session = {}\n        response = registration_complete(mock_request)\n        self.assertCalledOnce(\n            mock_render,\n            mock_request,\n            'registration/registration_complete.html',\n            {'email_address': None}\n        )\n        self.assertEquals(response, mock_render.return_value)\n\n\nclass MetaSecurityTest(django.test.TestCase):\n    def test_security_classes_exist(self):\n        assert_security_classes_exist(\n            self, __name__,\n            ['RedirectToFrontPageTest', 'RegistrationViewsTest', 'ResolverTestCase']\n        )\n\n"
  },
  {
    "path": "dirigible/user/urls.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\nfrom django.conf.urls import include, patterns, url\n\nfrom user.views import change_password, redirect_to_front_page\n\n\nurlpatterns = patterns(\n    '',\n    url(\n        r'^[^/]+/$',\n        redirect_to_front_page,\n        name=\"user_page\"\n    ),\n\n    url(\n        r'^(?P<username>[^/]+)/sheet/',\n        include('sheet.urls'),\n    ),\n\n    url(\n        r'^(?P<username>[^/]+)/change_password/$',\n        change_password,\n        name=\"change_password\"\n    ),\n\n)\n"
  },
  {
    "path": "dirigible/user/views.py",
    "content": "# Copyright (c) 2010 Resolver Systems Ltd, PythonAnywhere LLP\n# See LICENSE.md\n#\n\nfrom django.contrib.auth.decorators import login_required\nfrom django.contrib.auth.models import User\nfrom django.core.urlresolvers import resolve, reverse\nfrom django.http import Http404, HttpResponse, HttpResponseRedirect\nfrom django.shortcuts import render, render_to_response, get_object_or_404\nimport registration as django_registration\n\nfrom sheet.models import copy_sheet_to_user, Sheet\nfrom user.forms import DirigibleRegistrationForm\n\n\ndef copy_sheet_for_new_user_callback(next_url):\n    def _inner(user):\n        _, __, kwargs = resolve(next_url)\n        sheet_id = kwargs['sheet_id']\n        copy_sheet_to_user(Sheet.objects.get(id=sheet_id), user)\n    return _inner\n\n\ndef register(request):\n    form = DirigibleRegistrationForm(request.POST)\n    if form.is_valid():\n        email = form.cleaned_data[\"email\"]\n        request.session[\"email-address\"] = email\n\n    kwargs = {\n        'form_class': DirigibleRegistrationForm,\n    }\n    if 'next' in request.POST and request.POST['next']:\n        kwargs['profile_callback'] = copy_sheet_for_new_user_callback(\n            request.POST['next']\n        )\n    elif 'next' in request.GET and request.GET['next']:\n        kwargs['extra_context'] = {'next': request.GET['next']}\n\n    return django_registration.views.register(request, **kwargs)\n\n\ndef registration_complete(request):\n    return render(\n        request,\n        'registration/registration_complete.html',\n        {'email_address': request.session.get(\"email-address\")}\n    )\n\n\ndef redirect_to_front_page(request):\n    return HttpResponseRedirect(reverse('front_page'))\n\n\n@login_required\ndef user_dashboard(request):\n    sheets = Sheet.objects.filter(owner=request.user)\n    return render_to_response('user_page.html', {'user': request.user, 'sheets': sheets})\n\n\n@login_required\ndef change_password(request, username):\n    if request.user.username != username:\n        raise Http404('No User matches the given query.')\n    user = get_object_or_404(User, username=username)\n    current_password = request.POST.get('id_change_password_current_password', '')\n    if not user.check_password(current_password):\n        return HttpResponse('Current password incorrect.')\n\n    new_password1 = request.POST.get('id_change_password_new_password', '')\n    new_password2 = request.POST.get('id_change_password_new_password_again', '')\n    if '' in (new_password1, new_password2) or (new_password1 != new_password2):\n        return HttpResponse('Please provide the new password twice for confirmation.')\n    user.set_password(new_password1)\n    user.save()\n    return HttpResponse('Your password has been changed.')\n"
  },
  {
    "path": "documentation/.gitignore",
    "content": "_build\n\n"
  },
  {
    "path": "documentation/BeautifulSoup.py",
    "content": "\"\"\"Beautiful Soup\nElixir and Tonic\n\"The Screen-Scraper's Friend\"\nhttp://www.crummy.com/software/BeautifulSoup/\n\nBeautiful Soup parses a (possibly invalid) XML or HTML document into a\ntree representation. It provides methods and Pythonic idioms that make\nit easy to navigate, search, and modify the tree.\n\nA well-formed XML/HTML document yields a well-formed data\nstructure. An ill-formed XML/HTML document yields a correspondingly\nill-formed data structure. If your document is only locally\nwell-formed, you can use this library to find and process the\nwell-formed part of it.\n\nBeautiful Soup works with Python 2.2 and up. It has no external\ndependencies, but you'll have more success at converting data to UTF-8\nif you also install these three packages:\n\n* chardet, for auto-detecting character encodings\n  http://chardet.feedparser.org/\n* cjkcodecs and iconv_codec, which add more encodings to the ones supported\n  by stock Python.\n  http://cjkpython.i18n.org/\n\nBeautiful Soup defines classes for two main parsing strategies:\n\n * BeautifulStoneSoup, for parsing XML, SGML, or your domain-specific\n   language that kind of looks like XML.\n\n * BeautifulSoup, for parsing run-of-the-mill HTML code, be it valid\n   or invalid. This class has web browser-like heuristics for\n   obtaining a sensible parse tree in the face of common HTML errors.\n\nBeautiful Soup also defines a class (UnicodeDammit) for autodetecting\nthe encoding of an HTML or XML document, and converting it to\nUnicode. Much of this code is taken from Mark Pilgrim's Universal Feed Parser.\n\nFor more than you ever wanted to know about Beautiful Soup, see the\ndocumentation:\nhttp://www.crummy.com/software/BeautifulSoup/documentation.html\n\nHere, have some legalese:\n\nCopyright (c) 2004-2009, Leonard Richardson\n\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n  * Redistributions of source code must retain the above copyright\n    notice, this list of conditions and the following disclaimer.\n\n  * Redistributions in binary form must reproduce the above\n    copyright notice, this list of conditions and the following\n    disclaimer in the documentation and/or other materials provided\n    with the distribution.\n\n  * Neither the name of the the Beautiful Soup Consortium and All\n    Night Kosher Bakery nor the names of its contributors may be\n    used to endorse or promote products derived from this software\n    without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\nCONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\nEXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\nPROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\nPROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\nLIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\nNEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE, DAMMIT.\n\n\"\"\"\nfrom __future__ import generators\n\n__author__ = \"Leonard Richardson (leonardr@segfault.org)\"\n__version__ = \"3.1.0.1\"\n__copyright__ = \"Copyright (c) 2004-2009 Leonard Richardson\"\n__license__ = \"New-style BSD\"\n\nimport codecs\nimport markupbase\nimport types\nimport re\nfrom HTMLParser import HTMLParser, HTMLParseError\ntry:\n    from htmlentitydefs import name2codepoint\nexcept ImportError:\n    name2codepoint = {}\ntry:\n    set\nexcept NameError:\n    from sets import Set as set\n\n#These hacks make Beautiful Soup able to parse XML with namespaces\nmarkupbase._declname_match = re.compile(r'[a-zA-Z][-_.:a-zA-Z0-9]*\\s*').match\n\nDEFAULT_OUTPUT_ENCODING = \"utf-8\"\n\n# First, the classes that represent markup elements.\n\ndef sob(unicode, encoding):\n    \"\"\"Returns either the given Unicode string or its encoding.\"\"\"\n    if encoding is None:\n        return unicode\n    else:\n        return unicode.encode(encoding)\n\nclass PageElement:\n    \"\"\"Contains the navigational information for some part of the page\n    (either a tag or a piece of text)\"\"\"\n\n    def setup(self, parent=None, previous=None):\n        \"\"\"Sets up the initial relations between this element and\n        other elements.\"\"\"\n        self.parent = parent\n        self.previous = previous\n        self.next = None\n        self.previousSibling = None\n        self.nextSibling = None\n        if self.parent and self.parent.contents:\n            self.previousSibling = self.parent.contents[-1]\n            self.previousSibling.nextSibling = self\n\n    def replaceWith(self, replaceWith):\n        oldParent = self.parent\n        myIndex = self.parent.contents.index(self)\n        if hasattr(replaceWith, 'parent') and replaceWith.parent == self.parent:\n            # We're replacing this element with one of its siblings.\n            index = self.parent.contents.index(replaceWith)\n            if index and index < myIndex:\n                # Furthermore, it comes before this element. That\n                # means that when we extract it, the index of this\n                # element will change.\n                myIndex = myIndex - 1\n        self.extract()\n        oldParent.insert(myIndex, replaceWith)\n\n    def extract(self):\n        \"\"\"Destructively rips this element out of the tree.\"\"\"\n        if self.parent:\n            try:\n                self.parent.contents.remove(self)\n            except ValueError:\n                pass\n\n        #Find the two elements that would be next to each other if\n        #this element (and any children) hadn't been parsed. Connect\n        #the two.\n        lastChild = self._lastRecursiveChild()\n        nextElement = lastChild.next\n\n        if self.previous:\n            self.previous.next = nextElement\n        if nextElement:\n            nextElement.previous = self.previous\n        self.previous = None\n        lastChild.next = None\n\n        self.parent = None\n        if self.previousSibling:\n            self.previousSibling.nextSibling = self.nextSibling\n        if self.nextSibling:\n            self.nextSibling.previousSibling = self.previousSibling\n        self.previousSibling = self.nextSibling = None\n        return self\n\n    def _lastRecursiveChild(self):\n        \"Finds the last element beneath this object to be parsed.\"\n        lastChild = self\n        while hasattr(lastChild, 'contents') and lastChild.contents:\n            lastChild = lastChild.contents[-1]\n        return lastChild\n\n    def insert(self, position, newChild):\n        if (isinstance(newChild, basestring)\n            or isinstance(newChild, unicode)) \\\n            and not isinstance(newChild, NavigableString):\n            newChild = NavigableString(newChild)\n\n        position =  min(position, len(self.contents))\n        if hasattr(newChild, 'parent') and newChild.parent != None:\n            # We're 'inserting' an element that's already one\n            # of this object's children.\n            if newChild.parent == self:\n                index = self.find(newChild)\n                if index and index < position:\n                    # Furthermore we're moving it further down the\n                    # list of this object's children. That means that\n                    # when we extract this element, our target index\n                    # will jump down one.\n                    position = position - 1\n            newChild.extract()\n\n        newChild.parent = self\n        previousChild = None\n        if position == 0:\n            newChild.previousSibling = None\n            newChild.previous = self\n        else:\n            previousChild = self.contents[position-1]\n            newChild.previousSibling = previousChild\n            newChild.previousSibling.nextSibling = newChild\n            newChild.previous = previousChild._lastRecursiveChild()\n        if newChild.previous:\n            newChild.previous.next = newChild\n\n        newChildsLastElement = newChild._lastRecursiveChild()\n\n        if position >= len(self.contents):\n            newChild.nextSibling = None\n\n            parent = self\n            parentsNextSibling = None\n            while not parentsNextSibling:\n                parentsNextSibling = parent.nextSibling\n                parent = parent.parent\n                if not parent: # This is the last element in the document.\n                    break\n            if parentsNextSibling:\n                newChildsLastElement.next = parentsNextSibling\n            else:\n                newChildsLastElement.next = None\n        else:\n            nextChild = self.contents[position]\n            newChild.nextSibling = nextChild\n            if newChild.nextSibling:\n                newChild.nextSibling.previousSibling = newChild\n            newChildsLastElement.next = nextChild\n\n        if newChildsLastElement.next:\n            newChildsLastElement.next.previous = newChildsLastElement\n        self.contents.insert(position, newChild)\n\n    def append(self, tag):\n        \"\"\"Appends the given tag to the contents of this tag.\"\"\"\n        self.insert(len(self.contents), tag)\n\n    def findNext(self, name=None, attrs={}, text=None, **kwargs):\n        \"\"\"Returns the first item that matches the given criteria and\n        appears after this Tag in the document.\"\"\"\n        return self._findOne(self.findAllNext, name, attrs, text, **kwargs)\n\n    def findAllNext(self, name=None, attrs={}, text=None, limit=None,\n                    **kwargs):\n        \"\"\"Returns all items that match the given criteria and appear\n        after this Tag in the document.\"\"\"\n        return self._findAll(name, attrs, text, limit, self.nextGenerator,\n                             **kwargs)\n\n    def findNextSibling(self, name=None, attrs={}, text=None, **kwargs):\n        \"\"\"Returns the closest sibling to this Tag that matches the\n        given criteria and appears after this Tag in the document.\"\"\"\n        return self._findOne(self.findNextSiblings, name, attrs, text,\n                             **kwargs)\n\n    def findNextSiblings(self, name=None, attrs={}, text=None, limit=None,\n                         **kwargs):\n        \"\"\"Returns the siblings of this Tag that match the given\n        criteria and appear after this Tag in the document.\"\"\"\n        return self._findAll(name, attrs, text, limit,\n                             self.nextSiblingGenerator, **kwargs)\n    fetchNextSiblings = findNextSiblings # Compatibility with pre-3.x\n\n    def findPrevious(self, name=None, attrs={}, text=None, **kwargs):\n        \"\"\"Returns the first item that matches the given criteria and\n        appears before this Tag in the document.\"\"\"\n        return self._findOne(self.findAllPrevious, name, attrs, text, **kwargs)\n\n    def findAllPrevious(self, name=None, attrs={}, text=None, limit=None,\n                        **kwargs):\n        \"\"\"Returns all items that match the given criteria and appear\n        before this Tag in the document.\"\"\"\n        return self._findAll(name, attrs, text, limit, self.previousGenerator,\n                           **kwargs)\n    fetchPrevious = findAllPrevious # Compatibility with pre-3.x\n\n    def findPreviousSibling(self, name=None, attrs={}, text=None, **kwargs):\n        \"\"\"Returns the closest sibling to this Tag that matches the\n        given criteria and appears before this Tag in the document.\"\"\"\n        return self._findOne(self.findPreviousSiblings, name, attrs, text,\n                             **kwargs)\n\n    def findPreviousSiblings(self, name=None, attrs={}, text=None,\n                             limit=None, **kwargs):\n        \"\"\"Returns the siblings of this Tag that match the given\n        criteria and appear before this Tag in the document.\"\"\"\n        return self._findAll(name, attrs, text, limit,\n                             self.previousSiblingGenerator, **kwargs)\n    fetchPreviousSiblings = findPreviousSiblings # Compatibility with pre-3.x\n\n    def findParent(self, name=None, attrs={}, **kwargs):\n        \"\"\"Returns the closest parent of this Tag that matches the given\n        criteria.\"\"\"\n        # NOTE: We can't use _findOne because findParents takes a different\n        # set of arguments.\n        r = None\n        l = self.findParents(name, attrs, 1)\n        if l:\n            r = l[0]\n        return r\n\n    def findParents(self, name=None, attrs={}, limit=None, **kwargs):\n        \"\"\"Returns the parents of this Tag that match the given\n        criteria.\"\"\"\n\n        return self._findAll(name, attrs, None, limit, self.parentGenerator,\n                             **kwargs)\n    fetchParents = findParents # Compatibility with pre-3.x\n\n    #These methods do the real heavy lifting.\n\n    def _findOne(self, method, name, attrs, text, **kwargs):\n        r = None\n        l = method(name, attrs, text, 1, **kwargs)\n        if l:\n            r = l[0]\n        return r\n\n    def _findAll(self, name, attrs, text, limit, generator, **kwargs):\n        \"Iterates over a generator looking for things that match.\"\n\n        if isinstance(name, SoupStrainer):\n            strainer = name\n        else:\n            # Build a SoupStrainer\n            strainer = SoupStrainer(name, attrs, text, **kwargs)\n        results = ResultSet(strainer)\n        g = generator()\n        while True:\n            try:\n                i = g.next()\n            except StopIteration:\n                break\n            if i:\n                found = strainer.search(i)\n                if found:\n                    results.append(found)\n                    if limit and len(results) >= limit:\n                        break\n        return results\n\n    #These Generators can be used to navigate starting from both\n    #NavigableStrings and Tags.\n    def nextGenerator(self):\n        i = self\n        while i:\n            i = i.next\n            yield i\n\n    def nextSiblingGenerator(self):\n        i = self\n        while i:\n            i = i.nextSibling\n            yield i\n\n    def previousGenerator(self):\n        i = self\n        while i:\n            i = i.previous\n            yield i\n\n    def previousSiblingGenerator(self):\n        i = self\n        while i:\n            i = i.previousSibling\n            yield i\n\n    def parentGenerator(self):\n        i = self\n        while i:\n            i = i.parent\n            yield i\n\n    # Utility methods\n    def substituteEncoding(self, str, encoding=None):\n        encoding = encoding or \"utf-8\"\n        return str.replace(\"%SOUP-ENCODING%\", encoding)\n\n    def toEncoding(self, s, encoding=None):\n        \"\"\"Encodes an object to a string in some encoding, or to Unicode.\n        .\"\"\"\n        if isinstance(s, unicode):\n            if encoding:\n                s = s.encode(encoding)\n        elif isinstance(s, str):\n            if encoding:\n                s = s.encode(encoding)\n            else:\n                s = unicode(s)\n        else:\n            if encoding:\n                s  = self.toEncoding(str(s), encoding)\n            else:\n                s = unicode(s)\n        return s\n\nclass NavigableString(unicode, PageElement):\n\n    def __new__(cls, value):\n        \"\"\"Create a new NavigableString.\n\n        When unpickling a NavigableString, this method is called with\n        the string in DEFAULT_OUTPUT_ENCODING. That encoding needs to be\n        passed in to the superclass's __new__ or the superclass won't know\n        how to handle non-ASCII characters.\n        \"\"\"\n        if isinstance(value, unicode):\n            return unicode.__new__(cls, value)\n        return unicode.__new__(cls, value, DEFAULT_OUTPUT_ENCODING)\n\n    def __getnewargs__(self):\n        return (unicode(self),)\n\n    def __getattr__(self, attr):\n        \"\"\"text.string gives you text. This is for backwards\n        compatibility for Navigable*String, but for CData* it lets you\n        get the string without the CData wrapper.\"\"\"\n        if attr == 'string':\n            return self\n        else:\n            raise AttributeError, \"'%s' object has no attribute '%s'\" % (self.__class__.__name__, attr)\n\n    def encode(self, encoding=DEFAULT_OUTPUT_ENCODING):\n        return self.decode().encode(encoding)\n\n    def decodeGivenEventualEncoding(self, eventualEncoding):\n        return self\n\nclass CData(NavigableString):\n\n    def decodeGivenEventualEncoding(self, eventualEncoding):\n        return u'<![CDATA[' + self + u']]>'\n\nclass ProcessingInstruction(NavigableString):\n\n    def decodeGivenEventualEncoding(self, eventualEncoding):\n        output = self\n        if u'%SOUP-ENCODING%' in output:\n            output = self.substituteEncoding(output, eventualEncoding)\n        return u'<?' + output + u'?>'\n\nclass Comment(NavigableString):\n    def decodeGivenEventualEncoding(self, eventualEncoding):\n        return u'<!--' + self + u'-->'\n\nclass Declaration(NavigableString):\n    def decodeGivenEventualEncoding(self, eventualEncoding):\n        return u'<!' + self + u'>'\n\nclass Tag(PageElement):\n\n    \"\"\"Represents a found HTML tag with its attributes and contents.\"\"\"\n\n    def _invert(h):\n        \"Cheap function to invert a hash.\"\n        i = {}\n        for k,v in h.items():\n            i[v] = k\n        return i\n\n    XML_ENTITIES_TO_SPECIAL_CHARS = { \"apos\" : \"'\",\n                                      \"quot\" : '\"',\n                                      \"amp\" : \"&\",\n                                      \"lt\" : \"<\",\n                                      \"gt\" : \">\" }\n\n    XML_SPECIAL_CHARS_TO_ENTITIES = _invert(XML_ENTITIES_TO_SPECIAL_CHARS)\n\n    def _convertEntities(self, match):\n        \"\"\"Used in a call to re.sub to replace HTML, XML, and numeric\n        entities with the appropriate Unicode characters. If HTML\n        entities are being converted, any unrecognized entities are\n        escaped.\"\"\"\n        x = match.group(1)\n        if self.convertHTMLEntities and x in name2codepoint:\n            return unichr(name2codepoint[x])\n        elif x in self.XML_ENTITIES_TO_SPECIAL_CHARS:\n            if self.convertXMLEntities:\n                return self.XML_ENTITIES_TO_SPECIAL_CHARS[x]\n            else:\n                return u'&%s;' % x\n        elif len(x) > 0 and x[0] == '#':\n            # Handle numeric entities\n            if len(x) > 1 and x[1] == 'x':\n                return unichr(int(x[2:], 16))\n            else:\n                return unichr(int(x[1:]))\n\n        elif self.escapeUnrecognizedEntities:\n            return u'&amp;%s;' % x\n        else:\n            return u'&%s;' % x\n\n    def __init__(self, parser, name, attrs=None, parent=None,\n                 previous=None):\n        \"Basic constructor.\"\n\n        # We don't actually store the parser object: that lets extracted\n        # chunks be garbage-collected\n        self.parserClass = parser.__class__\n        self.isSelfClosing = parser.isSelfClosingTag(name)\n        self.name = name\n        if attrs == None:\n            attrs = []\n        self.attrs = attrs\n        self.contents = []\n        self.setup(parent, previous)\n        self.hidden = False\n        self.containsSubstitutions = False\n        self.convertHTMLEntities = parser.convertHTMLEntities\n        self.convertXMLEntities = parser.convertXMLEntities\n        self.escapeUnrecognizedEntities = parser.escapeUnrecognizedEntities\n\n        def convert(kval):\n            \"Converts HTML, XML and numeric entities in the attribute value.\"\n            k, val = kval\n            if val is None:\n                return kval\n            return (k, re.sub(\"&(#\\d+|#x[0-9a-fA-F]+|\\w+);\",\n                              self._convertEntities, val))\n        self.attrs = map(convert, self.attrs)\n\n    def get(self, key, default=None):\n        \"\"\"Returns the value of the 'key' attribute for the tag, or\n        the value given for 'default' if it doesn't have that\n        attribute.\"\"\"\n        return self._getAttrMap().get(key, default)\n\n    def has_key(self, key):\n        return self._getAttrMap().has_key(key)\n\n    def __getitem__(self, key):\n        \"\"\"tag[key] returns the value of the 'key' attribute for the tag,\n        and throws an exception if it's not there.\"\"\"\n        return self._getAttrMap()[key]\n\n    def __iter__(self):\n        \"Iterating over a tag iterates over its contents.\"\n        return iter(self.contents)\n\n    def __len__(self):\n        \"The length of a tag is the length of its list of contents.\"\n        return len(self.contents)\n\n    def __contains__(self, x):\n        return x in self.contents\n\n    def __nonzero__(self):\n        \"A tag is non-None even if it has no contents.\"\n        return True\n\n    def __setitem__(self, key, value):\n        \"\"\"Setting tag[key] sets the value of the 'key' attribute for the\n        tag.\"\"\"\n        self._getAttrMap()\n        self.attrMap[key] = value\n        found = False\n        for i in range(0, len(self.attrs)):\n            if self.attrs[i][0] == key:\n                self.attrs[i] = (key, value)\n                found = True\n        if not found:\n            self.attrs.append((key, value))\n        self._getAttrMap()[key] = value\n\n    def __delitem__(self, key):\n        \"Deleting tag[key] deletes all 'key' attributes for the tag.\"\n        for item in self.attrs:\n            if item[0] == key:\n                self.attrs.remove(item)\n                #We don't break because bad HTML can define the same\n                #attribute multiple times.\n            self._getAttrMap()\n            if self.attrMap.has_key(key):\n                del self.attrMap[key]\n\n    def __call__(self, *args, **kwargs):\n        \"\"\"Calling a tag like a function is the same as calling its\n        findAll() method. Eg. tag('a') returns a list of all the A tags\n        found within this tag.\"\"\"\n        return apply(self.findAll, args, kwargs)\n\n    def __getattr__(self, tag):\n        #print \"Getattr %s.%s\" % (self.__class__, tag)\n        if len(tag) > 3 and tag.rfind('Tag') == len(tag)-3:\n            return self.find(tag[:-3])\n        elif tag.find('__') != 0:\n            return self.find(tag)\n        raise AttributeError, \"'%s' object has no attribute '%s'\" % (self.__class__, tag)\n\n    def __eq__(self, other):\n        \"\"\"Returns true iff this tag has the same name, the same attributes,\n        and the same contents (recursively) as the given tag.\n\n        NOTE: right now this will return false if two tags have the\n        same attributes in a different order. Should this be fixed?\"\"\"\n        if not hasattr(other, 'name') or not hasattr(other, 'attrs') or not hasattr(other, 'contents') or self.name != other.name or self.attrs != other.attrs or len(self) != len(other):\n            return False\n        for i in range(0, len(self.contents)):\n            if self.contents[i] != other.contents[i]:\n                return False\n        return True\n\n    def __ne__(self, other):\n        \"\"\"Returns true iff this tag is not identical to the other tag,\n        as defined in __eq__.\"\"\"\n        return not self == other\n\n    def __repr__(self, encoding=DEFAULT_OUTPUT_ENCODING):\n        \"\"\"Renders this tag as a string.\"\"\"\n        return self.decode(eventualEncoding=encoding)\n\n    BARE_AMPERSAND_OR_BRACKET = re.compile(\"([<>]|\"\n                                           + \"&(?!#\\d+;|#x[0-9a-fA-F]+;|\\w+;)\"\n                                           + \")\")\n\n    def _sub_entity(self, x):\n        \"\"\"Used with a regular expression to substitute the\n        appropriate XML entity for an XML special character.\"\"\"\n        return \"&\" + self.XML_SPECIAL_CHARS_TO_ENTITIES[x.group(0)[0]] + \";\"\n\n    def __unicode__(self):\n        return self.decode()\n\n    def __str__(self):\n        return self.encode()\n\n    def encode(self, encoding=DEFAULT_OUTPUT_ENCODING,\n               prettyPrint=False, indentLevel=0):\n        return self.decode(prettyPrint, indentLevel, encoding).encode(encoding)\n\n    def decode(self, prettyPrint=False, indentLevel=0,\n               eventualEncoding=DEFAULT_OUTPUT_ENCODING):\n        \"\"\"Returns a string or Unicode representation of this tag and\n        its contents. To get Unicode, pass None for encoding.\"\"\"\n\n        attrs = []\n        if self.attrs:\n            for key, val in self.attrs:\n                fmt = '%s=\"%s\"'\n                if isString(val):\n                    if (self.containsSubstitutions\n                        and eventualEncoding is not None\n                        and '%SOUP-ENCODING%' in val):\n                        val = self.substituteEncoding(val, eventualEncoding)\n\n                    # The attribute value either:\n                    #\n                    # * Contains no embedded double quotes or single quotes.\n                    #   No problem: we enclose it in double quotes.\n                    # * Contains embedded single quotes. No problem:\n                    #   double quotes work here too.\n                    # * Contains embedded double quotes. No problem:\n                    #   we enclose it in single quotes.\n                    # * Embeds both single _and_ double quotes. This\n                    #   can't happen naturally, but it can happen if\n                    #   you modify an attribute value after parsing\n                    #   the document. Now we have a bit of a\n                    #   problem. We solve it by enclosing the\n                    #   attribute in single quotes, and escaping any\n                    #   embedded single quotes to XML entities.\n                    if '\"' in val:\n                        fmt = \"%s='%s'\"\n                        if \"'\" in val:\n                            # TODO: replace with apos when\n                            # appropriate.\n                            val = val.replace(\"'\", \"&squot;\")\n\n                    # Now we're okay w/r/t quotes. But the attribute\n                    # value might also contain angle brackets, or\n                    # ampersands that aren't part of entities. We need\n                    # to escape those to XML entities too.\n                    val = self.BARE_AMPERSAND_OR_BRACKET.sub(self._sub_entity, val)\n                if val is None:\n                    # Handle boolean attributes.\n                    decoded = key\n                else:\n                    decoded = fmt % (key, val)\n                attrs.append(decoded)\n        close = ''\n        closeTag = ''\n        if self.isSelfClosing:\n            close = ' /'\n        else:\n            closeTag = '</%s>' % self.name\n\n        indentTag, indentContents = 0, 0\n        if prettyPrint:\n            indentTag = indentLevel\n            space = (' ' * (indentTag-1))\n            indentContents = indentTag + 1\n        contents = self.decodeContents(prettyPrint, indentContents,\n                                       eventualEncoding)\n        if self.hidden:\n            s = contents\n        else:\n            s = []\n            attributeString = ''\n            if attrs:\n                attributeString = ' ' + ' '.join(attrs)\n            if prettyPrint:\n                s.append(space)\n            s.append('<%s%s%s>' % (self.name, attributeString, close))\n            if prettyPrint:\n                s.append(\"\\n\")\n            s.append(contents)\n            if prettyPrint and contents and contents[-1] != \"\\n\":\n                s.append(\"\\n\")\n            if prettyPrint and closeTag:\n                s.append(space)\n            s.append(closeTag)\n            if prettyPrint and closeTag and self.nextSibling:\n                s.append(\"\\n\")\n            s = ''.join(s)\n        return s\n\n    def decompose(self):\n        \"\"\"Recursively destroys the contents of this tree.\"\"\"\n        contents = [i for i in self.contents]\n        for i in contents:\n            if isinstance(i, Tag):\n                i.decompose()\n            else:\n                i.extract()\n        self.extract()\n\n    def prettify(self, encoding=DEFAULT_OUTPUT_ENCODING):\n        return self.encode(encoding, True)\n\n    def encodeContents(self, encoding=DEFAULT_OUTPUT_ENCODING,\n                       prettyPrint=False, indentLevel=0):\n        return self.decodeContents(prettyPrint, indentLevel).encode(encoding)\n\n    def decodeContents(self, prettyPrint=False, indentLevel=0,\n                       eventualEncoding=DEFAULT_OUTPUT_ENCODING):\n        \"\"\"Renders the contents of this tag as a string in the given\n        encoding. If encoding is None, returns a Unicode string..\"\"\"\n        s=[]\n        for c in self:\n            text = None\n            if isinstance(c, NavigableString):\n                text = c.decodeGivenEventualEncoding(eventualEncoding)\n            elif isinstance(c, Tag):\n                s.append(c.decode(prettyPrint, indentLevel, eventualEncoding))\n            if text and prettyPrint:\n                text = text.strip()\n            if text:\n                if prettyPrint:\n                    s.append(\" \" * (indentLevel-1))\n                s.append(text)\n                if prettyPrint:\n                    s.append(\"\\n\")\n        return ''.join(s)\n\n    #Soup methods\n\n    def find(self, name=None, attrs={}, recursive=True, text=None,\n             **kwargs):\n        \"\"\"Return only the first child of this Tag matching the given\n        criteria.\"\"\"\n        r = None\n        l = self.findAll(name, attrs, recursive, text, 1, **kwargs)\n        if l:\n            r = l[0]\n        return r\n    findChild = find\n\n    def findAll(self, name=None, attrs={}, recursive=True, text=None,\n                limit=None, **kwargs):\n        \"\"\"Extracts a list of Tag objects that match the given\n        criteria.  You can specify the name of the Tag and any\n        attributes you want the Tag to have.\n\n        The value of a key-value pair in the 'attrs' map can be a\n        string, a list of strings, a regular expression object, or a\n        callable that takes a string and returns whether or not the\n        string matches for some custom definition of 'matches'. The\n        same is true of the tag name.\"\"\"\n        generator = self.recursiveChildGenerator\n        if not recursive:\n            generator = self.childGenerator\n        return self._findAll(name, attrs, text, limit, generator, **kwargs)\n    findChildren = findAll\n\n    # Pre-3.x compatibility methods. Will go away in 4.0.\n    first = find\n    fetch = findAll\n\n    def fetchText(self, text=None, recursive=True, limit=None):\n        return self.findAll(text=text, recursive=recursive, limit=limit)\n\n    def firstText(self, text=None, recursive=True):\n        return self.find(text=text, recursive=recursive)\n\n    # 3.x compatibility methods. Will go away in 4.0.\n    def renderContents(self, encoding=DEFAULT_OUTPUT_ENCODING,\n                       prettyPrint=False, indentLevel=0):\n        if encoding is None:\n            return self.decodeContents(prettyPrint, indentLevel, encoding)\n        else:\n            return self.encodeContents(encoding, prettyPrint, indentLevel)\n\n\n    #Private methods\n\n    def _getAttrMap(self):\n        \"\"\"Initializes a map representation of this tag's attributes,\n        if not already initialized.\"\"\"\n        if not getattr(self, 'attrMap'):\n            self.attrMap = {}\n            for (key, value) in self.attrs:\n                self.attrMap[key] = value\n        return self.attrMap\n\n    #Generator methods\n    def recursiveChildGenerator(self):\n        if not len(self.contents):\n            raise StopIteration\n        stopNode = self._lastRecursiveChild().next\n        current = self.contents[0]\n        while current is not stopNode:\n            yield current\n            current = current.next\n\n    def childGenerator(self):\n        if not len(self.contents):\n            raise StopIteration\n        current = self.contents[0]\n        while current:\n            yield current\n            current = current.nextSibling\n        raise StopIteration\n\n# Next, a couple classes to represent queries and their results.\nclass SoupStrainer:\n    \"\"\"Encapsulates a number of ways of matching a markup element (tag or\n    text).\"\"\"\n\n    def __init__(self, name=None, attrs={}, text=None, **kwargs):\n        self.name = name\n        if isString(attrs):\n            kwargs['class'] = attrs\n            attrs = None\n        if kwargs:\n            if attrs:\n                attrs = attrs.copy()\n                attrs.update(kwargs)\n            else:\n                attrs = kwargs\n        self.attrs = attrs\n        self.text = text\n\n    def __str__(self):\n        if self.text:\n            return self.text\n        else:\n            return \"%s|%s\" % (self.name, self.attrs)\n\n    def searchTag(self, markupName=None, markupAttrs={}):\n        found = None\n        markup = None\n        if isinstance(markupName, Tag):\n            markup = markupName\n            markupAttrs = markup\n        callFunctionWithTagData = callable(self.name) \\\n                                and not isinstance(markupName, Tag)\n\n        if (not self.name) \\\n               or callFunctionWithTagData \\\n               or (markup and self._matches(markup, self.name)) \\\n               or (not markup and self._matches(markupName, self.name)):\n            if callFunctionWithTagData:\n                match = self.name(markupName, markupAttrs)\n            else:\n                match = True\n                markupAttrMap = None\n                for attr, matchAgainst in self.attrs.items():\n                    if not markupAttrMap:\n                         if hasattr(markupAttrs, 'get'):\n                            markupAttrMap = markupAttrs\n                         else:\n                            markupAttrMap = {}\n                            for k,v in markupAttrs:\n                                markupAttrMap[k] = v\n                    attrValue = markupAttrMap.get(attr)\n                    if not self._matches(attrValue, matchAgainst):\n                        match = False\n                        break\n            if match:\n                if markup:\n                    found = markup\n                else:\n                    found = markupName\n        return found\n\n    def search(self, markup):\n        #print 'looking for %s in %s' % (self, markup)\n        found = None\n        # If given a list of items, scan it for a text element that\n        # matches.\n        if isList(markup) and not isinstance(markup, Tag):\n            for element in markup:\n                if isinstance(element, NavigableString) \\\n                       and self.search(element):\n                    found = element\n                    break\n        # If it's a Tag, make sure its name or attributes match.\n        # Don't bother with Tags if we're searching for text.\n        elif isinstance(markup, Tag):\n            if not self.text:\n                found = self.searchTag(markup)\n        # If it's text, make sure the text matches.\n        elif isinstance(markup, NavigableString) or \\\n                 isString(markup):\n            if self._matches(markup, self.text):\n                found = markup\n        else:\n            raise Exception, \"I don't know how to match against a %s\" \\\n                  % markup.__class__\n        return found\n\n    def _matches(self, markup, matchAgainst):\n        #print \"Matching %s against %s\" % (markup, matchAgainst)\n        result = False\n        if matchAgainst == True and type(matchAgainst) == types.BooleanType:\n            result = markup != None\n        elif callable(matchAgainst):\n            result = matchAgainst(markup)\n        else:\n            #Custom match methods take the tag as an argument, but all\n            #other ways of matching match the tag name as a string.\n            if isinstance(markup, Tag):\n                markup = markup.name\n            if markup is not None and not isString(markup):\n                markup = unicode(markup)\n            #Now we know that chunk is either a string, or None.\n            if hasattr(matchAgainst, 'match'):\n                # It's a regexp object.\n                result = markup and matchAgainst.search(markup)\n            elif (isList(matchAgainst)\n                  and (markup is not None or not isString(matchAgainst))):\n                result = markup in matchAgainst\n            elif hasattr(matchAgainst, 'items'):\n                result = markup.has_key(matchAgainst)\n            elif matchAgainst and isString(markup):\n                if isinstance(markup, unicode):\n                    matchAgainst = unicode(matchAgainst)\n                else:\n                    matchAgainst = str(matchAgainst)\n\n            if not result:\n                result = matchAgainst == markup\n        return result\n\nclass ResultSet(list):\n    \"\"\"A ResultSet is just a list that keeps track of the SoupStrainer\n    that created it.\"\"\"\n    def __init__(self, source):\n        list.__init__([])\n        self.source = source\n\n# Now, some helper functions.\n\ndef isList(l):\n    \"\"\"Convenience method that works with all 2.x versions of Python\n    to determine whether or not something is listlike.\"\"\"\n    return ((hasattr(l, '__iter__') and not isString(l))\n            or (type(l) in (types.ListType, types.TupleType)))\n\ndef isString(s):\n    \"\"\"Convenience method that works with all 2.x versions of Python\n    to determine whether or not something is stringlike.\"\"\"\n    try:\n        return isinstance(s, unicode) or isinstance(s, basestring)\n    except NameError:\n        return isinstance(s, str)\n\ndef buildTagMap(default, *args):\n    \"\"\"Turns a list of maps, lists, or scalars into a single map.\n    Used to build the SELF_CLOSING_TAGS, NESTABLE_TAGS, and\n    NESTING_RESET_TAGS maps out of lists and partial maps.\"\"\"\n    built = {}\n    for portion in args:\n        if hasattr(portion, 'items'):\n            #It's a map. Merge it.\n            for k,v in portion.items():\n                built[k] = v\n        elif isList(portion) and not isString(portion):\n            #It's a list. Map each item to the default.\n            for k in portion:\n                built[k] = default\n        else:\n            #It's a scalar. Map it to the default.\n            built[portion] = default\n    return built\n\n# Now, the parser classes.\n\nclass HTMLParserBuilder(HTMLParser):\n\n    def __init__(self, soup):\n        HTMLParser.__init__(self)\n        self.soup = soup\n\n    # We inherit feed() and reset().\n\n    def handle_starttag(self, name, attrs):\n        if name == 'meta':\n            self.soup.extractCharsetFromMeta(attrs)\n        else:\n            self.soup.unknown_starttag(name, attrs)\n\n    def handle_endtag(self, name):\n        self.soup.unknown_endtag(name)\n\n    def handle_data(self, content):\n        self.soup.handle_data(content)\n\n    def _toStringSubclass(self, text, subclass):\n        \"\"\"Adds a certain piece of text to the tree as a NavigableString\n        subclass.\"\"\"\n        self.soup.endData()\n        self.handle_data(text)\n        self.soup.endData(subclass)\n\n    def handle_pi(self, text):\n        \"\"\"Handle a processing instruction as a ProcessingInstruction\n        object, possibly one with a %SOUP-ENCODING% slot into which an\n        encoding will be plugged later.\"\"\"\n        if text[:3] == \"xml\":\n            text = u\"xml version='1.0' encoding='%SOUP-ENCODING%'\"\n        self._toStringSubclass(text, ProcessingInstruction)\n\n    def handle_comment(self, text):\n        \"Handle comments as Comment objects.\"\n        self._toStringSubclass(text, Comment)\n\n    def handle_charref(self, ref):\n        \"Handle character references as data.\"\n        if self.soup.convertEntities:\n            data = unichr(int(ref))\n        else:\n            data = '&#%s;' % ref\n        self.handle_data(data)\n\n    def handle_entityref(self, ref):\n        \"\"\"Handle entity references as data, possibly converting known\n        HTML and/or XML entity references to the corresponding Unicode\n        characters.\"\"\"\n        data = None\n        if self.soup.convertHTMLEntities:\n            try:\n                data = unichr(name2codepoint[ref])\n            except KeyError:\n                pass\n\n        if not data and self.soup.convertXMLEntities:\n                data = self.soup.XML_ENTITIES_TO_SPECIAL_CHARS.get(ref)\n\n        if not data and self.soup.convertHTMLEntities and \\\n            not self.soup.XML_ENTITIES_TO_SPECIAL_CHARS.get(ref):\n                # TODO: We've got a problem here. We're told this is\n                # an entity reference, but it's not an XML entity\n                # reference or an HTML entity reference. Nonetheless,\n                # the logical thing to do is to pass it through as an\n                # unrecognized entity reference.\n                #\n                # Except: when the input is \"&carol;\" this function\n                # will be called with input \"carol\". When the input is\n                # \"AT&T\", this function will be called with input\n                # \"T\". We have no way of knowing whether a semicolon\n                # was present originally, so we don't know whether\n                # this is an unknown entity or just a misplaced\n                # ampersand.\n                #\n                # The more common case is a misplaced ampersand, so I\n                # escape the ampersand and omit the trailing semicolon.\n                data = \"&amp;%s\" % ref\n        if not data:\n            # This case is different from the one above, because we\n            # haven't already gone through a supposedly comprehensive\n            # mapping of entities to Unicode characters. We might not\n            # have gone through any mapping at all. So the chances are\n            # very high that this is a real entity, and not a\n            # misplaced ampersand.\n            data = \"&%s;\" % ref\n        self.handle_data(data)\n\n    def handle_decl(self, data):\n        \"Handle DOCTYPEs and the like as Declaration objects.\"\n        self._toStringSubclass(data, Declaration)\n\n    def parse_declaration(self, i):\n        \"\"\"Treat a bogus SGML declaration as raw data. Treat a CDATA\n        declaration as a CData object.\"\"\"\n        j = None\n        if self.rawdata[i:i+9] == '<![CDATA[':\n             k = self.rawdata.find(']]>', i)\n             if k == -1:\n                 k = len(self.rawdata)\n             data = self.rawdata[i+9:k]\n             j = k+3\n             self._toStringSubclass(data, CData)\n        else:\n            try:\n                j = HTMLParser.parse_declaration(self, i)\n            except HTMLParseError:\n                toHandle = self.rawdata[i:]\n                self.handle_data(toHandle)\n                j = i + len(toHandle)\n        return j\n\n\nclass BeautifulStoneSoup(Tag):\n\n    \"\"\"This class contains the basic parser and search code. It defines\n    a parser that knows nothing about tag behavior except for the\n    following:\n\n      You can't close a tag without closing all the tags it encloses.\n      That is, \"<foo><bar></foo>\" actually means\n      \"<foo><bar></bar></foo>\".\n\n    [Another possible explanation is \"<foo><bar /></foo>\", but since\n    this class defines no SELF_CLOSING_TAGS, it will never use that\n    explanation.]\n\n    This class is useful for parsing XML or made-up markup languages,\n    or when BeautifulSoup makes an assumption counter to what you were\n    expecting.\"\"\"\n\n    SELF_CLOSING_TAGS = {}\n    NESTABLE_TAGS = {}\n    RESET_NESTING_TAGS = {}\n    QUOTE_TAGS = {}\n    PRESERVE_WHITESPACE_TAGS = []\n\n    MARKUP_MASSAGE = [(re.compile('(<[^<>]*)/>'),\n                       lambda x: x.group(1) + ' />'),\n                      (re.compile('<!\\s+([^<>]*)>'),\n                       lambda x: '<!' + x.group(1) + '>')\n                      ]\n\n    ROOT_TAG_NAME = u'[document]'\n\n    HTML_ENTITIES = \"html\"\n    XML_ENTITIES = \"xml\"\n    XHTML_ENTITIES = \"xhtml\"\n    # TODO: This only exists for backwards-compatibility\n    ALL_ENTITIES = XHTML_ENTITIES\n\n    # Used when determining whether a text node is all whitespace and\n    # can be replaced with a single space. A text node that contains\n    # fancy Unicode spaces (usually non-breaking) should be left\n    # alone.\n    STRIP_ASCII_SPACES = { 9: None, 10: None, 12: None, 13: None, 32: None, }\n\n    def __init__(self, markup=\"\", parseOnlyThese=None, fromEncoding=None,\n                 markupMassage=True, smartQuotesTo=XML_ENTITIES,\n                 convertEntities=None, selfClosingTags=None, isHTML=False,\n                 builder=HTMLParserBuilder):\n        \"\"\"The Soup object is initialized as the 'root tag', and the\n        provided markup (which can be a string or a file-like object)\n        is fed into the underlying parser.\n\n        HTMLParser will process most bad HTML, and the BeautifulSoup\n        class has some tricks for dealing with some HTML that kills\n        HTMLParser, but Beautiful Soup can nonetheless choke or lose data\n        if your data uses self-closing tags or declarations\n        incorrectly.\n\n        By default, Beautiful Soup uses regexes to sanitize input,\n        avoiding the vast majority of these problems. If the problems\n        don't apply to you, pass in False for markupMassage, and\n        you'll get better performance.\n\n        The default parser massage techniques fix the two most common\n        instances of invalid HTML that choke HTMLParser:\n\n         <br/> (No space between name of closing tag and tag close)\n         <! --Comment--> (Extraneous whitespace in declaration)\n\n        You can pass in a custom list of (RE object, replace method)\n        tuples to get Beautiful Soup to scrub your input the way you\n        want.\"\"\"\n\n        self.parseOnlyThese = parseOnlyThese\n        self.fromEncoding = fromEncoding\n        self.smartQuotesTo = smartQuotesTo\n        self.convertEntities = convertEntities\n        # Set the rules for how we'll deal with the entities we\n        # encounter\n        if self.convertEntities:\n            # It doesn't make sense to convert encoded characters to\n            # entities even while you're converting entities to Unicode.\n            # Just convert it all to Unicode.\n            self.smartQuotesTo = None\n            if convertEntities == self.HTML_ENTITIES:\n                self.convertXMLEntities = False\n                self.convertHTMLEntities = True\n                self.escapeUnrecognizedEntities = True\n            elif convertEntities == self.XHTML_ENTITIES:\n                self.convertXMLEntities = True\n                self.convertHTMLEntities = True\n                self.escapeUnrecognizedEntities = False\n            elif convertEntities == self.XML_ENTITIES:\n                self.convertXMLEntities = True\n                self.convertHTMLEntities = False\n                self.escapeUnrecognizedEntities = False\n        else:\n            self.convertXMLEntities = False\n            self.convertHTMLEntities = False\n            self.escapeUnrecognizedEntities = False\n\n        self.instanceSelfClosingTags = buildTagMap(None, selfClosingTags)\n        self.builder = builder(self)\n        self.reset()\n\n        if hasattr(markup, 'read'):        # It's a file-type object.\n            markup = markup.read()\n        self.markup = markup\n        self.markupMassage = markupMassage\n        try:\n            self._feed(isHTML=isHTML)\n        except StopParsing:\n            pass\n        self.markup = None                 # The markup can now be GCed.\n        self.builder = None                # So can the builder.\n\n    def _feed(self, inDocumentEncoding=None, isHTML=False):\n        # Convert the document to Unicode.\n        markup = self.markup\n        if isinstance(markup, unicode):\n            if not hasattr(self, 'originalEncoding'):\n                self.originalEncoding = None\n        else:\n            dammit = UnicodeDammit\\\n                     (markup, [self.fromEncoding, inDocumentEncoding],\n                      smartQuotesTo=self.smartQuotesTo, isHTML=isHTML)\n            markup = dammit.unicode\n            self.originalEncoding = dammit.originalEncoding\n            self.declaredHTMLEncoding = dammit.declaredHTMLEncoding\n        if markup:\n            if self.markupMassage:\n                if not isList(self.markupMassage):\n                    self.markupMassage = self.MARKUP_MASSAGE\n                for fix, m in self.markupMassage:\n                    markup = fix.sub(m, markup)\n                # TODO: We get rid of markupMassage so that the\n                # soup object can be deepcopied later on. Some\n                # Python installations can't copy regexes. If anyone\n                # was relying on the existence of markupMassage, this\n                # might cause problems.\n                del(self.markupMassage)\n        self.builder.reset()\n\n        self.builder.feed(markup)\n        # Close out any unfinished strings and close all the open tags.\n        self.endData()\n        while self.currentTag.name != self.ROOT_TAG_NAME:\n            self.popTag()\n\n    def isSelfClosingTag(self, name):\n        \"\"\"Returns true iff the given string is the name of a\n        self-closing tag according to this parser.\"\"\"\n        return self.SELF_CLOSING_TAGS.has_key(name) \\\n               or self.instanceSelfClosingTags.has_key(name)\n\n    def reset(self):\n        Tag.__init__(self, self, self.ROOT_TAG_NAME)\n        self.hidden = 1\n        self.builder.reset()\n        self.currentData = []\n        self.currentTag = None\n        self.tagStack = []\n        self.quoteStack = []\n        self.pushTag(self)\n\n    def popTag(self):\n        tag = self.tagStack.pop()\n        # Tags with just one string-owning child get the child as a\n        # 'string' property, so that soup.tag.string is shorthand for\n        # soup.tag.contents[0]\n        if len(self.currentTag.contents) == 1 and \\\n           isinstance(self.currentTag.contents[0], NavigableString):\n            self.currentTag.string = self.currentTag.contents[0]\n\n        #print \"Pop\", tag.name\n        if self.tagStack:\n            self.currentTag = self.tagStack[-1]\n        return self.currentTag\n\n    def pushTag(self, tag):\n        #print \"Push\", tag.name\n        if self.currentTag:\n            self.currentTag.contents.append(tag)\n        self.tagStack.append(tag)\n        self.currentTag = self.tagStack[-1]\n\n    def endData(self, containerClass=NavigableString):\n        if self.currentData:\n            currentData = u''.join(self.currentData)\n            if (currentData.translate(self.STRIP_ASCII_SPACES) == '' and\n                not set([tag.name for tag in self.tagStack]).intersection(\n                    self.PRESERVE_WHITESPACE_TAGS)):\n                if '\\n' in currentData:\n                    currentData = '\\n'\n                else:\n                    currentData = ' '\n            self.currentData = []\n            if self.parseOnlyThese and len(self.tagStack) <= 1 and \\\n                   (not self.parseOnlyThese.text or \\\n                    not self.parseOnlyThese.search(currentData)):\n                return\n            o = containerClass(currentData)\n            o.setup(self.currentTag, self.previous)\n            if self.previous:\n                self.previous.next = o\n            self.previous = o\n            self.currentTag.contents.append(o)\n\n\n    def _popToTag(self, name, inclusivePop=True):\n        \"\"\"Pops the tag stack up to and including the most recent\n        instance of the given tag. If inclusivePop is false, pops the tag\n        stack up to but *not* including the most recent instqance of\n        the given tag.\"\"\"\n        #print \"Popping to %s\" % name\n        if name == self.ROOT_TAG_NAME:\n            return\n\n        numPops = 0\n        mostRecentTag = None\n        for i in range(len(self.tagStack)-1, 0, -1):\n            if name == self.tagStack[i].name:\n                numPops = len(self.tagStack)-i\n                break\n        if not inclusivePop:\n            numPops = numPops - 1\n\n        for i in range(0, numPops):\n            mostRecentTag = self.popTag()\n        return mostRecentTag\n\n    def _smartPop(self, name):\n\n        \"\"\"We need to pop up to the previous tag of this type, unless\n        one of this tag's nesting reset triggers comes between this\n        tag and the previous tag of this type, OR unless this tag is a\n        generic nesting trigger and another generic nesting trigger\n        comes between this tag and the previous tag of this type.\n\n        Examples:\n         <p>Foo<b>Bar *<p>* should pop to 'p', not 'b'.\n         <p>Foo<table>Bar *<p>* should pop to 'table', not 'p'.\n         <p>Foo<table><tr>Bar *<p>* should pop to 'tr', not 'p'.\n\n         <li><ul><li> *<li>* should pop to 'ul', not the first 'li'.\n         <tr><table><tr> *<tr>* should pop to 'table', not the first 'tr'\n         <td><tr><td> *<td>* should pop to 'tr', not the first 'td'\n        \"\"\"\n\n        nestingResetTriggers = self.NESTABLE_TAGS.get(name)\n        isNestable = nestingResetTriggers != None\n        isResetNesting = self.RESET_NESTING_TAGS.has_key(name)\n        popTo = None\n        inclusive = True\n        for i in range(len(self.tagStack)-1, 0, -1):\n            p = self.tagStack[i]\n            if (not p or p.name == name) and not isNestable:\n                #Non-nestable tags get popped to the top or to their\n                #last occurance.\n                popTo = name\n                break\n            if (nestingResetTriggers != None\n                and p.name in nestingResetTriggers) \\\n                or (nestingResetTriggers == None and isResetNesting\n                    and self.RESET_NESTING_TAGS.has_key(p.name)):\n\n                #If we encounter one of the nesting reset triggers\n                #peculiar to this tag, or we encounter another tag\n                #that causes nesting to reset, pop up to but not\n                #including that tag.\n                popTo = p.name\n                inclusive = False\n                break\n            p = p.parent\n        if popTo:\n            self._popToTag(popTo, inclusive)\n\n    def unknown_starttag(self, name, attrs, selfClosing=0):\n        #print \"Start tag %s: %s\" % (name, attrs)\n        if self.quoteStack:\n            #This is not a real tag.\n            #print \"<%s> is not real!\" % name\n            attrs = ''.join(map(lambda(x, y): ' %s=\"%s\"' % (x, y), attrs))\n            self.handle_data('<%s%s>' % (name, attrs))\n            return\n        self.endData()\n\n        if not self.isSelfClosingTag(name) and not selfClosing:\n            self._smartPop(name)\n\n        if self.parseOnlyThese and len(self.tagStack) <= 1 \\\n               and (self.parseOnlyThese.text or not self.parseOnlyThese.searchTag(name, attrs)):\n            return\n\n        tag = Tag(self, name, attrs, self.currentTag, self.previous)\n        if self.previous:\n            self.previous.next = tag\n        self.previous = tag\n        self.pushTag(tag)\n        if selfClosing or self.isSelfClosingTag(name):\n            self.popTag()\n        if name in self.QUOTE_TAGS:\n            #print \"Beginning quote (%s)\" % name\n            self.quoteStack.append(name)\n            self.literal = 1\n        return tag\n\n    def unknown_endtag(self, name):\n        #print \"End tag %s\" % name\n        if self.quoteStack and self.quoteStack[-1] != name:\n            #This is not a real end tag.\n            #print \"</%s> is not real!\" % name\n            self.handle_data('</%s>' % name)\n            return\n        self.endData()\n        self._popToTag(name)\n        if self.quoteStack and self.quoteStack[-1] == name:\n            self.quoteStack.pop()\n            self.literal = (len(self.quoteStack) > 0)\n\n    def handle_data(self, data):\n        self.currentData.append(data)\n\n    def extractCharsetFromMeta(self, attrs):\n        self.unknown_starttag('meta', attrs)\n\n\nclass BeautifulSoup(BeautifulStoneSoup):\n\n    \"\"\"This parser knows the following facts about HTML:\n\n    * Some tags have no closing tag and should be interpreted as being\n      closed as soon as they are encountered.\n\n    * The text inside some tags (ie. 'script') may contain tags which\n      are not really part of the document and which should be parsed\n      as text, not tags. If you want to parse the text as tags, you can\n      always fetch it and parse it explicitly.\n\n    * Tag nesting rules:\n\n      Most tags can't be nested at all. For instance, the occurance of\n      a <p> tag should implicitly close the previous <p> tag.\n\n       <p>Para1<p>Para2\n        should be transformed into:\n       <p>Para1</p><p>Para2\n\n      Some tags can be nested arbitrarily. For instance, the occurance\n      of a <blockquote> tag should _not_ implicitly close the previous\n      <blockquote> tag.\n\n       Alice said: <blockquote>Bob said: <blockquote>Blah\n        should NOT be transformed into:\n       Alice said: <blockquote>Bob said: </blockquote><blockquote>Blah\n\n      Some tags can be nested, but the nesting is reset by the\n      interposition of other tags. For instance, a <tr> tag should\n      implicitly close the previous <tr> tag within the same <table>,\n      but not close a <tr> tag in another table.\n\n       <table><tr>Blah<tr>Blah\n        should be transformed into:\n       <table><tr>Blah</tr><tr>Blah\n        but,\n       <tr>Blah<table><tr>Blah\n        should NOT be transformed into\n       <tr>Blah<table></tr><tr>Blah\n\n    Differing assumptions about tag nesting rules are a major source\n    of problems with the BeautifulSoup class. If BeautifulSoup is not\n    treating as nestable a tag your page author treats as nestable,\n    try ICantBelieveItsBeautifulSoup, MinimalSoup, or\n    BeautifulStoneSoup before writing your own subclass.\"\"\"\n\n    def __init__(self, *args, **kwargs):\n        if not kwargs.has_key('smartQuotesTo'):\n            kwargs['smartQuotesTo'] = self.HTML_ENTITIES\n        kwargs['isHTML'] = True\n        BeautifulStoneSoup.__init__(self, *args, **kwargs)\n\n    SELF_CLOSING_TAGS = buildTagMap(None,\n                                    ['br' , 'hr', 'input', 'img', 'meta',\n                                    'spacer', 'link', 'frame', 'base'])\n\n    PRESERVE_WHITESPACE_TAGS = set(['pre', 'textarea'])\n\n    QUOTE_TAGS = {'script' : None, 'textarea' : None}\n\n    #According to the HTML standard, each of these inline tags can\n    #contain another tag of the same type. Furthermore, it's common\n    #to actually use these tags this way.\n    NESTABLE_INLINE_TAGS = ['span', 'font', 'q', 'object', 'bdo', 'sub', 'sup',\n                            'center']\n\n    #According to the HTML standard, these block tags can contain\n    #another tag of the same type. Furthermore, it's common\n    #to actually use these tags this way.\n    NESTABLE_BLOCK_TAGS = ['blockquote', 'div', 'fieldset', 'ins', 'del']\n\n    #Lists can contain other lists, but there are restrictions.\n    NESTABLE_LIST_TAGS = { 'ol' : [],\n                           'ul' : [],\n                           'li' : ['ul', 'ol'],\n                           'dl' : [],\n                           'dd' : ['dl'],\n                           'dt' : ['dl'] }\n\n    #Tables can contain other tables, but there are restrictions.\n    NESTABLE_TABLE_TAGS = {'table' : [],\n                           'tr' : ['table', 'tbody', 'tfoot', 'thead'],\n                           'td' : ['tr'],\n                           'th' : ['tr'],\n                           'thead' : ['table'],\n                           'tbody' : ['table'],\n                           'tfoot' : ['table'],\n                           }\n\n    NON_NESTABLE_BLOCK_TAGS = ['address', 'form', 'p', 'pre']\n\n    #If one of these tags is encountered, all tags up to the next tag of\n    #this type are popped.\n    RESET_NESTING_TAGS = buildTagMap(None, NESTABLE_BLOCK_TAGS, 'noscript',\n                                     NON_NESTABLE_BLOCK_TAGS,\n                                     NESTABLE_LIST_TAGS,\n                                     NESTABLE_TABLE_TAGS)\n\n    NESTABLE_TAGS = buildTagMap([], NESTABLE_INLINE_TAGS, NESTABLE_BLOCK_TAGS,\n                                NESTABLE_LIST_TAGS, NESTABLE_TABLE_TAGS)\n\n    # Used to detect the charset in a META tag; see start_meta\n    CHARSET_RE = re.compile(\"((^|;)\\s*charset=)([^;]*)\", re.M)\n\n    def extractCharsetFromMeta(self, attrs):\n        \"\"\"Beautiful Soup can detect a charset included in a META tag,\n        try to convert the document to that charset, and re-parse the\n        document from the beginning.\"\"\"\n        httpEquiv = None\n        contentType = None\n        contentTypeIndex = None\n        tagNeedsEncodingSubstitution = False\n\n        for i in range(0, len(attrs)):\n            key, value = attrs[i]\n            key = key.lower()\n            if key == 'http-equiv':\n                httpEquiv = value\n            elif key == 'content':\n                contentType = value\n                contentTypeIndex = i\n\n        if httpEquiv and contentType: # It's an interesting meta tag.\n            match = self.CHARSET_RE.search(contentType)\n            if match:\n                if (self.declaredHTMLEncoding is not None or\n                    self.originalEncoding == self.fromEncoding):\n                    # An HTML encoding was sniffed while converting\n                    # the document to Unicode, or an HTML encoding was\n                    # sniffed during a previous pass through the\n                    # document, or an encoding was specified\n                    # explicitly and it worked. Rewrite the meta tag.\n                    def rewrite(match):\n                        return match.group(1) + \"%SOUP-ENCODING%\"\n                    newAttr = self.CHARSET_RE.sub(rewrite, contentType)\n                    attrs[contentTypeIndex] = (attrs[contentTypeIndex][0],\n                                               newAttr)\n                    tagNeedsEncodingSubstitution = True\n                else:\n                    # This is our first pass through the document.\n                    # Go through it again with the encoding information.\n                    newCharset = match.group(3)\n                    if newCharset and newCharset != self.originalEncoding:\n                        self.declaredHTMLEncoding = newCharset\n                        self._feed(self.declaredHTMLEncoding)\n                        raise StopParsing\n                    pass\n        tag = self.unknown_starttag(\"meta\", attrs)\n        if tag and tagNeedsEncodingSubstitution:\n            tag.containsSubstitutions = True\n\n\nclass StopParsing(Exception):\n    pass\n\nclass ICantBelieveItsBeautifulSoup(BeautifulSoup):\n\n    \"\"\"The BeautifulSoup class is oriented towards skipping over\n    common HTML errors like unclosed tags. However, sometimes it makes\n    errors of its own. For instance, consider this fragment:\n\n     <b>Foo<b>Bar</b></b>\n\n    This is perfectly valid (if bizarre) HTML. However, the\n    BeautifulSoup class will implicitly close the first b tag when it\n    encounters the second 'b'. It will think the author wrote\n    \"<b>Foo<b>Bar\", and didn't close the first 'b' tag, because\n    there's no real-world reason to bold something that's already\n    bold. When it encounters '</b></b>' it will close two more 'b'\n    tags, for a grand total of three tags closed instead of two. This\n    can throw off the rest of your document structure. The same is\n    true of a number of other tags, listed below.\n\n    It's much more common for someone to forget to close a 'b' tag\n    than to actually use nested 'b' tags, and the BeautifulSoup class\n    handles the common case. This class handles the not-co-common\n    case: where you can't believe someone wrote what they did, but\n    it's valid HTML and BeautifulSoup screwed up by assuming it\n    wouldn't be.\"\"\"\n\n    I_CANT_BELIEVE_THEYRE_NESTABLE_INLINE_TAGS = \\\n     ['em', 'big', 'i', 'small', 'tt', 'abbr', 'acronym', 'strong',\n      'cite', 'code', 'dfn', 'kbd', 'samp', 'strong', 'var', 'b',\n      'big']\n\n    I_CANT_BELIEVE_THEYRE_NESTABLE_BLOCK_TAGS = ['noscript']\n\n    NESTABLE_TAGS = buildTagMap([], BeautifulSoup.NESTABLE_TAGS,\n                                I_CANT_BELIEVE_THEYRE_NESTABLE_BLOCK_TAGS,\n                                I_CANT_BELIEVE_THEYRE_NESTABLE_INLINE_TAGS)\n\nclass MinimalSoup(BeautifulSoup):\n    \"\"\"The MinimalSoup class is for parsing HTML that contains\n    pathologically bad markup. It makes no assumptions about tag\n    nesting, but it does know which tags are self-closing, that\n    <script> tags contain Javascript and should not be parsed, that\n    META tags may contain encoding information, and so on.\n\n    This also makes it better for subclassing than BeautifulStoneSoup\n    or BeautifulSoup.\"\"\"\n\n    RESET_NESTING_TAGS = buildTagMap('noscript')\n    NESTABLE_TAGS = {}\n\nclass BeautifulSOAP(BeautifulStoneSoup):\n    \"\"\"This class will push a tag with only a single string child into\n    the tag's parent as an attribute. The attribute's name is the tag\n    name, and the value is the string child. An example should give\n    the flavor of the change:\n\n    <foo><bar>baz</bar></foo>\n     =>\n    <foo bar=\"baz\"><bar>baz</bar></foo>\n\n    You can then access fooTag['bar'] instead of fooTag.barTag.string.\n\n    This is, of course, useful for scraping structures that tend to\n    use subelements instead of attributes, such as SOAP messages. Note\n    that it modifies its input, so don't print the modified version\n    out.\n\n    I'm not sure how many people really want to use this class; let me\n    know if you do. Mainly I like the name.\"\"\"\n\n    def popTag(self):\n        if len(self.tagStack) > 1:\n            tag = self.tagStack[-1]\n            parent = self.tagStack[-2]\n            parent._getAttrMap()\n            if (isinstance(tag, Tag) and len(tag.contents) == 1 and\n                isinstance(tag.contents[0], NavigableString) and\n                not parent.attrMap.has_key(tag.name)):\n                parent[tag.name] = tag.contents[0]\n        BeautifulStoneSoup.popTag(self)\n\n#Enterprise class names! It has come to our attention that some people\n#think the names of the Beautiful Soup parser classes are too silly\n#and \"unprofessional\" for use in enterprise screen-scraping. We feel\n#your pain! For such-minded folk, the Beautiful Soup Consortium And\n#All-Night Kosher Bakery recommends renaming this file to\n#\"RobustParser.py\" (or, in cases of extreme enterprisiness,\n#\"RobustParserBeanInterface.class\") and using the following\n#enterprise-friendly class aliases:\nclass RobustXMLParser(BeautifulStoneSoup):\n    pass\nclass RobustHTMLParser(BeautifulSoup):\n    pass\nclass RobustWackAssHTMLParser(ICantBelieveItsBeautifulSoup):\n    pass\nclass RobustInsanelyWackAssHTMLParser(MinimalSoup):\n    pass\nclass SimplifyingSOAPParser(BeautifulSOAP):\n    pass\n\n######################################################\n#\n# Bonus library: Unicode, Dammit\n#\n# This class forces XML data into a standard format (usually to UTF-8\n# or Unicode).  It is heavily based on code from Mark Pilgrim's\n# Universal Feed Parser. It does not rewrite the XML or HTML to\n# reflect a new encoding: that happens in BeautifulStoneSoup.handle_pi\n# (XML) and BeautifulSoup.start_meta (HTML).\n\n# Autodetects character encodings.\n# Download from http://chardet.feedparser.org/\ntry:\n    import chardet\n#    import chardet.constants\n#    chardet.constants._debug = 1\nexcept ImportError:\n    chardet = None\n\n# cjkcodecs and iconv_codec make Python know about more character encodings.\n# Both are available from http://cjkpython.i18n.org/\n# They're built in if you use Python 2.4.\ntry:\n    import cjkcodecs.aliases\nexcept ImportError:\n    pass\ntry:\n    import iconv_codec\nexcept ImportError:\n    pass\n\nclass UnicodeDammit:\n    \"\"\"A class for detecting the encoding of a *ML document and\n    converting it to a Unicode string. If the source encoding is\n    windows-1252, can replace MS smart quotes with their HTML or XML\n    equivalents.\"\"\"\n\n    # This dictionary maps commonly seen values for \"charset\" in HTML\n    # meta tags to the corresponding Python codec names. It only covers\n    # values that aren't in Python's aliases and can't be determined\n    # by the heuristics in find_codec.\n    CHARSET_ALIASES = { \"macintosh\" : \"mac-roman\",\n                        \"x-sjis\" : \"shift-jis\" }\n\n    def __init__(self, markup, overrideEncodings=[],\n                 smartQuotesTo='xml', isHTML=False):\n        self.declaredHTMLEncoding = None\n        self.markup, documentEncoding, sniffedEncoding = \\\n                     self._detectEncoding(markup, isHTML)\n        self.smartQuotesTo = smartQuotesTo\n        self.triedEncodings = []\n        if markup == '' or isinstance(markup, unicode):\n            self.originalEncoding = None\n            self.unicode = unicode(markup)\n            return\n\n        u = None\n        for proposedEncoding in overrideEncodings:\n            u = self._convertFrom(proposedEncoding)\n            if u: break\n        if not u:\n            for proposedEncoding in (documentEncoding, sniffedEncoding):\n                u = self._convertFrom(proposedEncoding)\n                if u: break\n\n        # If no luck and we have auto-detection library, try that:\n        if not u and chardet and not isinstance(self.markup, unicode):\n            u = self._convertFrom(chardet.detect(self.markup)['encoding'])\n\n        # As a last resort, try utf-8 and windows-1252:\n        if not u:\n            for proposed_encoding in (\"utf-8\", \"windows-1252\"):\n                u = self._convertFrom(proposed_encoding)\n                if u: break\n\n        self.unicode = u\n        if not u: self.originalEncoding = None\n\n    def _subMSChar(self, match):\n        \"\"\"Changes a MS smart quote character to an XML or HTML\n        entity.\"\"\"\n        orig = match.group(1)\n        sub = self.MS_CHARS.get(orig)\n        if type(sub) == types.TupleType:\n            if self.smartQuotesTo == 'xml':\n                sub = '&#x'.encode() + sub[1].encode() + ';'.encode()\n            else:\n                sub = '&'.encode() + sub[0].encode() + ';'.encode()\n        else:\n            sub = sub.encode()\n        return sub\n\n    def _convertFrom(self, proposed):\n        proposed = self.find_codec(proposed)\n        if not proposed or proposed in self.triedEncodings:\n            return None\n        self.triedEncodings.append(proposed)\n        markup = self.markup\n\n        # Convert smart quotes to HTML if coming from an encoding\n        # that might have them.\n        if self.smartQuotesTo and proposed.lower() in(\"windows-1252\",\n                                                      \"iso-8859-1\",\n                                                      \"iso-8859-2\"):\n            smart_quotes_re = \"([\\x80-\\x9f])\"\n            smart_quotes_compiled = re.compile(smart_quotes_re)\n            markup = smart_quotes_compiled.sub(self._subMSChar, markup)\n\n        try:\n            # print \"Trying to convert document to %s\" % proposed\n            u = self._toUnicode(markup, proposed)\n            self.markup = u\n            self.originalEncoding = proposed\n        except Exception, e:\n            # print \"That didn't work!\"\n            # print e\n            return None\n        #print \"Correct encoding: %s\" % proposed\n        return self.markup\n\n    def _toUnicode(self, data, encoding):\n        '''Given a string and its encoding, decodes the string into Unicode.\n        %encoding is a string recognized by encodings.aliases'''\n\n        # strip Byte Order Mark (if present)\n        if (len(data) >= 4) and (data[:2] == '\\xfe\\xff') \\\n               and (data[2:4] != '\\x00\\x00'):\n            encoding = 'utf-16be'\n            data = data[2:]\n        elif (len(data) >= 4) and (data[:2] == '\\xff\\xfe') \\\n                 and (data[2:4] != '\\x00\\x00'):\n            encoding = 'utf-16le'\n            data = data[2:]\n        elif data[:3] == '\\xef\\xbb\\xbf':\n            encoding = 'utf-8'\n            data = data[3:]\n        elif data[:4] == '\\x00\\x00\\xfe\\xff':\n            encoding = 'utf-32be'\n            data = data[4:]\n        elif data[:4] == '\\xff\\xfe\\x00\\x00':\n            encoding = 'utf-32le'\n            data = data[4:]\n        newdata = unicode(data, encoding)\n        return newdata\n\n    def _detectEncoding(self, xml_data, isHTML=False):\n        \"\"\"Given a document, tries to detect its XML encoding.\"\"\"\n        xml_encoding = sniffed_xml_encoding = None\n        try:\n            if xml_data[:4] == '\\x4c\\x6f\\xa7\\x94':\n                # EBCDIC\n                xml_data = self._ebcdic_to_ascii(xml_data)\n            elif xml_data[:4] == '\\x00\\x3c\\x00\\x3f':\n                # UTF-16BE\n                sniffed_xml_encoding = 'utf-16be'\n                xml_data = unicode(xml_data, 'utf-16be').encode('utf-8')\n            elif (len(xml_data) >= 4) and (xml_data[:2] == '\\xfe\\xff') \\\n                     and (xml_data[2:4] != '\\x00\\x00'):\n                # UTF-16BE with BOM\n                sniffed_xml_encoding = 'utf-16be'\n                xml_data = unicode(xml_data[2:], 'utf-16be').encode('utf-8')\n            elif xml_data[:4] == '\\x3c\\x00\\x3f\\x00':\n                # UTF-16LE\n                sniffed_xml_encoding = 'utf-16le'\n                xml_data = unicode(xml_data, 'utf-16le').encode('utf-8')\n            elif (len(xml_data) >= 4) and (xml_data[:2] == '\\xff\\xfe') and \\\n                     (xml_data[2:4] != '\\x00\\x00'):\n                # UTF-16LE with BOM\n                sniffed_xml_encoding = 'utf-16le'\n                xml_data = unicode(xml_data[2:], 'utf-16le').encode('utf-8')\n            elif xml_data[:4] == '\\x00\\x00\\x00\\x3c':\n                # UTF-32BE\n                sniffed_xml_encoding = 'utf-32be'\n                xml_data = unicode(xml_data, 'utf-32be').encode('utf-8')\n            elif xml_data[:4] == '\\x3c\\x00\\x00\\x00':\n                # UTF-32LE\n                sniffed_xml_encoding = 'utf-32le'\n                xml_data = unicode(xml_data, 'utf-32le').encode('utf-8')\n            elif xml_data[:4] == '\\x00\\x00\\xfe\\xff':\n                # UTF-32BE with BOM\n                sniffed_xml_encoding = 'utf-32be'\n                xml_data = unicode(xml_data[4:], 'utf-32be').encode('utf-8')\n            elif xml_data[:4] == '\\xff\\xfe\\x00\\x00':\n                # UTF-32LE with BOM\n                sniffed_xml_encoding = 'utf-32le'\n                xml_data = unicode(xml_data[4:], 'utf-32le').encode('utf-8')\n            elif xml_data[:3] == '\\xef\\xbb\\xbf':\n                # UTF-8 with BOM\n                sniffed_xml_encoding = 'utf-8'\n                xml_data = unicode(xml_data[3:], 'utf-8').encode('utf-8')\n            else:\n                sniffed_xml_encoding = 'ascii'\n                pass\n        except:\n            xml_encoding_match = None\n        xml_encoding_re = '^<\\?.*encoding=[\\'\"](.*?)[\\'\"].*\\?>'.encode()\n        xml_encoding_match = re.compile(xml_encoding_re).match(xml_data)\n        if not xml_encoding_match and isHTML:\n            meta_re = '<\\s*meta[^>]+charset=([^>]*?)[;\\'\">]'.encode()\n            regexp = re.compile(meta_re, re.I)\n            xml_encoding_match = regexp.search(xml_data)\n        if xml_encoding_match is not None:\n            xml_encoding = xml_encoding_match.groups()[0].decode(\n                'ascii').lower()\n            if isHTML:\n                self.declaredHTMLEncoding = xml_encoding\n            if sniffed_xml_encoding and \\\n               (xml_encoding in ('iso-10646-ucs-2', 'ucs-2', 'csunicode',\n                                 'iso-10646-ucs-4', 'ucs-4', 'csucs4',\n                                 'utf-16', 'utf-32', 'utf_16', 'utf_32',\n                                 'utf16', 'u16')):\n                xml_encoding = sniffed_xml_encoding\n        return xml_data, xml_encoding, sniffed_xml_encoding\n\n\n    def find_codec(self, charset):\n        return self._codec(self.CHARSET_ALIASES.get(charset, charset)) \\\n               or (charset and self._codec(charset.replace(\"-\", \"\"))) \\\n               or (charset and self._codec(charset.replace(\"-\", \"_\"))) \\\n               or charset\n\n    def _codec(self, charset):\n        if not charset: return charset\n        codec = None\n        try:\n            codecs.lookup(charset)\n            codec = charset\n        except (LookupError, ValueError):\n            pass\n        return codec\n\n    EBCDIC_TO_ASCII_MAP = None\n    def _ebcdic_to_ascii(self, s):\n        c = self.__class__\n        if not c.EBCDIC_TO_ASCII_MAP:\n            emap = (0,1,2,3,156,9,134,127,151,141,142,11,12,13,14,15,\n                    16,17,18,19,157,133,8,135,24,25,146,143,28,29,30,31,\n                    128,129,130,131,132,10,23,27,136,137,138,139,140,5,6,7,\n                    144,145,22,147,148,149,150,4,152,153,154,155,20,21,158,26,\n                    32,160,161,162,163,164,165,166,167,168,91,46,60,40,43,33,\n                    38,169,170,171,172,173,174,175,176,177,93,36,42,41,59,94,\n                    45,47,178,179,180,181,182,183,184,185,124,44,37,95,62,63,\n                    186,187,188,189,190,191,192,193,194,96,58,35,64,39,61,34,\n                    195,97,98,99,100,101,102,103,104,105,196,197,198,199,200,\n                    201,202,106,107,108,109,110,111,112,113,114,203,204,205,\n                    206,207,208,209,126,115,116,117,118,119,120,121,122,210,\n                    211,212,213,214,215,216,217,218,219,220,221,222,223,224,\n                    225,226,227,228,229,230,231,123,65,66,67,68,69,70,71,72,\n                    73,232,233,234,235,236,237,125,74,75,76,77,78,79,80,81,\n                    82,238,239,240,241,242,243,92,159,83,84,85,86,87,88,89,\n                    90,244,245,246,247,248,249,48,49,50,51,52,53,54,55,56,57,\n                    250,251,252,253,254,255)\n            import string\n            c.EBCDIC_TO_ASCII_MAP = string.maketrans( \\\n            ''.join(map(chr, range(256))), ''.join(map(chr, emap)))\n        return s.translate(c.EBCDIC_TO_ASCII_MAP)\n\n    MS_CHARS = { '\\x80' : ('euro', '20AC'),\n                 '\\x81' : ' ',\n                 '\\x82' : ('sbquo', '201A'),\n                 '\\x83' : ('fnof', '192'),\n                 '\\x84' : ('bdquo', '201E'),\n                 '\\x85' : ('hellip', '2026'),\n                 '\\x86' : ('dagger', '2020'),\n                 '\\x87' : ('Dagger', '2021'),\n                 '\\x88' : ('circ', '2C6'),\n                 '\\x89' : ('permil', '2030'),\n                 '\\x8A' : ('Scaron', '160'),\n                 '\\x8B' : ('lsaquo', '2039'),\n                 '\\x8C' : ('OElig', '152'),\n                 '\\x8D' : '?',\n                 '\\x8E' : ('#x17D', '17D'),\n                 '\\x8F' : '?',\n                 '\\x90' : '?',\n                 '\\x91' : ('lsquo', '2018'),\n                 '\\x92' : ('rsquo', '2019'),\n                 '\\x93' : ('ldquo', '201C'),\n                 '\\x94' : ('rdquo', '201D'),\n                 '\\x95' : ('bull', '2022'),\n                 '\\x96' : ('ndash', '2013'),\n                 '\\x97' : ('mdash', '2014'),\n                 '\\x98' : ('tilde', '2DC'),\n                 '\\x99' : ('trade', '2122'),\n                 '\\x9a' : ('scaron', '161'),\n                 '\\x9b' : ('rsaquo', '203A'),\n                 '\\x9c' : ('oelig', '153'),\n                 '\\x9d' : '?',\n                 '\\x9e' : ('#x17E', '17E'),\n                 '\\x9f' : ('Yuml', ''),}\n\n#######################################################################\n\n\n#By default, act as an HTML pretty-printer.\nif __name__ == '__main__':\n    import sys\n    soup = BeautifulSoup(sys.stdin)\n    print soup.prettify()\n"
  },
  {
    "path": "documentation/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/Dirigible.qhcp\"\n\t@echo \"To view the help file:\"\n\t@echo \"# assistant -collectionFile $(BUILDDIR)/qthelp/Dirigible.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/Dirigible\"\n\t@echo \"# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Dirigible\"\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": "documentation/_static/file-to-force-git-to-keep-empty-dir",
    "content": ""
  },
  {
    "path": "documentation/_templates/layout.html",
    "content": "{% extends \"!layout.html\" %}\n\n"
  },
  {
    "path": "documentation/builtins.rst",
    "content": "Built-in Dirigible classes and functions\n========================================\n\nThe usercode you enter on the right-hand side of your Dirigible spreadsheet is there to *recalculate* the spreadsheet -- in other words, it takes a grid of *constants* like ``\"23\"`` and *formulae* like ``\"=A1+A2\"``, and turns it into a grid of *values* by evaluating the formulae in the context of the constants.\n\nThe default usercode does just enough of this to make Dirigible act like a traditional spreadsheet, but you can make it do much more by writing your own usercode.  This page describes the tools that Dirigible provides to do that; you can also use :doc:`other Python modules <python-modules>`.\n\nThe worksheet object\n--------------------\n\n.. data:: worksheet\n\n    A variable bound to a :class:`Worksheet` object, populated with the data that you have set by editing the spreadsheet grid.  At the start of the usercode, ``worksheet`` will hold what you have entered; the formulae and constants.  It is the responsibility of the usercode to \"recalculate\" the spreadsheet to work out the resulting values.\n\n\n.. class:: Worksheet\n\n    An object that represents the contents of the spreadsheet grid.  One is created for you by the Dirigible runtime -- you can access it using the :const:`worksheet` variable -- but you can create your own too.  :class:`Worksheet` objects are also returned by the function :func:`run_worksheet`.\n\n    The most useful thing you can do with a :class:`Worksheet` is access its cells, using any of the following forms::\n\n        worksheet[2, 3]\n        worksheet['B', 3]\n        worksheet['B3']\n        worksheet.B3\n\n\n    All of the above give you a :class:`Cell` object representing the data that has been put into the spreadsheet grid at location B3.\n\n    You can also ask the :class:`Worksheet` for a :class:`CellRange`:\n\n    .. method:: Worksheet.cell_range(self, start_or_string_cellrange, end=None)\n\n        If only ``start_or_string_cellrange`` is specified, treats it as the kind of cell range reference you might make in a formula (eg. ``F2:M13``) and returns the appropriate :class:`CellRange` object.  If two parameters are given, they are treated as two corners of the cell range, and can be either string cell references (eg. ``\"B3\"``) or \"column, row\" numerical tuples (eg. ``(2, 3)``).  Thus,\n        the following are equivalent::\n\n            my_range = worksheet.cell_range('B3:C10')\n            my_range = worksheet.cell_range('B3', 'C10')\n            my_range = worksheet.cell_range('B3', (3, 10))\n            my_range = worksheet.cell_range((2, 3), 'C10')\n            my_range = worksheet.cell_range((2, 3), (3, 10))\n\n\n    .. attribute:: Worksheet.bounds\n\n        Returns the outside coordinates for the used area of the worksheet, ie the leftmost-column, the highest-row, the rightmost-column and the lowest-row.  This is returned as a special ``tuple`` (left, top, right, bottom) of :class:`Bounds`, which has properties ``.left``, ``.top``, ``.right`` and ``.bottom``.  Thus, ``worksheet.bounds.top`` works.\n\n\nOther available classes\n-----------------------\n\n.. class:: Cell\n\n    An object that represents the contents of one cell in the spreadsheet grid.  It has a number of useful attributes:\n\n    .. attribute:: Cell.formula\n\n        The text that you can see and change when you edit the cell in the spreadsheet grid.  You can also set it from anywhere in your own usercode.  It is evaluated by calls to :func:`load_constants` or by :func:`evaluate_formulae`, depending on whether it starts with ``=``.\n\n    .. attribute:: Cell.value\n\n        The Python object that is the result of executing the cell's :attr:`formula`, used when evaluating other cells that depend on this cell.  If the :attr:`formula` does not start with ``=``, then this attribute will be set by :func:`load_constants`; if it *does* start with ``=``, then it will be set by :func:`evaluate_formulae`.  You can also set it yourself from your own code.  Setting a cell's :attr:`value` sets its :attr:`formatted_value` to a string representation of the new value.\n\n    .. attribute:: Cell.formatted_value\n\n        The text that you see for the cell in the grid when you are *not* editing it, so long as the cell has no :attr:`error`.  Normally this is a string representation of the :attr:`value`, but if you want to override the formatting then you can set it yourself from your own usercode.\n\n    .. attribute:: Cell.error\n\n        If this is set, the cell shows an error icon in the grid, like this:\n\n        .. raw:: html\n\n            <img src=\"/static/dirigible/images/error.gif\" alt=\"A Dirigible error\" /><br/><br/>\n\n\n        The contents of this field appear in a help bubble when the mouse \"hovers\" over the cell.\n\n\n    .. method:: Cell.clear\n\n        Sets the :attr:`Cell.formula` and :attr:`Cell.error` to ``None``, the :attr:`Cell.value` to an instance of :class:`Undefined`, and the :attr:`Cell.formatted_value` to an empty string.\n\n\n.. class:: CellRange\n\n    An object that represents a rectangular block of cells within a worksheet.  Created using the :meth:`Worksheet.cell_range` convenience function.\n\n    .. attribute:: CellRange.top\n\n        The top of the :class:`CellRange` -- that is, its lowest row number.\n\n    .. attribute:: CellRange.left\n\n        The left of the :class:`CellRange` -- that is, its lowest column number.\n\n    .. attribute:: CellRange.bottom\n\n        The bottom of the :class:`CellRange` -- that is, its highest row number.\n\n    .. attribute:: CellRange.right\n\n        The right of the :class:`CellRange` -- that is, its highest column number.\n\n    .. method:: CellRange.__len__(self, other)\n\n        Normally accessed by calling ``len(cell_range)``.  Returns the total number of cells in the range, including empty ones.\n\n    .. attribute:: CellRange.locations\n\n        Returns an interator over each cell location in the :class:`CellRange`, running left-to-right, top-to-bottom.  For example, for the range ``B3:D4``, this would return the sequence (2, 3), (2, 3), (4, 3), (2, 4), (3, 4), (4, 4).\n\n    .. method:: CellRange.__iter__\n\n        Returns each value in the cells within the :class:`CellRange`, in the same order as :meth:`CellRange.locations`.  If a cell is empty, returns an :class:`Undefined` object.  Normally used with code such as the following::\n\n            result = 5\n            for value in worksheet.cell_range(\"B3:D4\"):\n                result = result * value + 2\n\n    .. attribute:: CellRange.cells\n\n        Returns the :class:`Cell` objects within the :class:`CellRange`, in the same order as :meth:`CellRange.locations`.\n\n    .. attribute:: CellRange.locations_and_cells\n\n        Returns a sequence of tuples comprising locations in the :class:`CellRange`, and the :class:`Cell` objects at those locations, in the same order as :meth:`CellRange.locations`.\n\n    .. method:: __getitem__(self, location)\n    .. method:: __setitem__(self, location, value)\n\n        Get and set the :class:`Cell` objects at particular relative locations within the :class:`CellRange`.  The ``location`` must be a tuple of two numbers; (1, 1) is the top left of the cell range.\n\n        For example, the following two lines would have the same effect::\n\n            worksheet.D3.value = 23\n            worksheet.cell_range(\"C2:D9\")[2, 2].value = 23\n\n    .. method:: clear(self)\n\n        Clears the :class:`CellRange` by calling meth:`Cell.clear` on each of its cells.\n\n    .. method:: CellRange.__eq__(self, other)\n\n        Returns true iff ``other`` is a :class:`CellRange` with the same ``worksheet`` and top, left, bottom and right.\n\n    .. method:: CellRange.__ne__(self, other)\n\n        Identical to ``not`` :meth:`CellRange.__ne__`\n\n\n\n.. class:: Undefined\n\n    If the :attr:`Cell.value` of a :class:`Cell` has not been set yet, then it returns an instance of this class.\n\n\n\n.. class:: Bounds\n\n    Subclass of ``tuple`` representing the outside bounds of the used part of the worksheet.  Usually accessed via :attr:`Worksheet.bounds`. Contains 4 integers in the order ``(leftmost-column, topmost-row, rightmost-col, bottom-row))``.  These may also be accessed as ``.left``, ``.top``, ``.right``, ``.bottom``.\n\n\nSpecial spreadsheet functions\n-----------------------------\n\n.. function:: load_constants(worksheet)\n\n    Goes through all of the cells in the given ``worksheet``, looking at each one's :attr:`~.Cell.formula`.  If this *does not* start with ``=``, then this function works out what type it is -- number or string -- converts it to that type, and puts the result into the cell's :attr:`~.Cell.value` attribute.  For example, the formula ``\"23\"`` would be converted into the integer value ``23``, the formula ``\"Hello, world\"`` would just be copied directly over as a string, and the formula \"=A1+A2\" would be ignored by this function.\n\n    Formulae that start with ``=`` are handled by :func:`evaluate_formulae`.\n\n\n.. function:: evaluate_formulae(worksheet)\n\n    Goes through all of the cells in the given ``worksheet``, looking at each one's :attr:`~.Cell.formula`.  If it *does* start with ``=``, then it is evaluated and the results are put into the the cell's :attr:`~.Cell.value` attribute.\n\n    There are a number of interesting features of this function:\n\n    * Dependency analysis.  The cells in the worksheet are evaluated in an order such that if cell *x* depends on cell *y*, then *y*'s formula is evaluated first.  For example, if A2 contains the formula ``=A1+2`` and A1 contains the formula ``=A3``, then A3 will be evaluated, then A1, then A2.\n    * Cycle elimination.  If a \"cycle\" of mutually-dependent cells is found, they are not evaluated and the details of the cycle is put into each cell's :attr:`~.Cell.error`.  An example of a cycle: A1 has formula ``=A2`` and cell A2 has the formula ``=A1``.\n    * Parallel recalculation.  If cells have no dependency, then they *may* be recalculated in parallel -- that is, at the same time.  This is useful when some cells take a long time to calculate -- for example, if they are calling :func:`run_worksheet` to execute calculations in another Dirigible spreadsheet -- because it allows your spreadsheet to get on with other calculations while awaiting a response.\n\n\n.. function:: run_worksheet(worksheet_url, overrides=None)\n\n    Accesses the target Dirigible worksheet at ``worksheet_url``, recalculates it, and returns the resulting :class:`Worksheet` object.  ``overrides`` is a dictionary containing formulae to put into the target worksheet before doing the recalculation; to override cell B3 with the value ``89`` you would use the dictionary ``{ (2, 3) : 89 }``\n\n    This function helps you re-use your work.  For example, if you created a spreadsheet to calculate the value of a financial product, you might then use :func:`run_worksheet` in a different \"portfolio\" spreadsheet.  The portfolio would be a list of holdings of different products, and for each one if would use the original spreadsheet to work out the product's value.  The details of each product would be passed from spreadsheet to spreadsheet using the ``overrides`` dictionary.\n"
  },
  {
    "path": "documentation/conf.py",
    "content": "# -*- coding: utf-8 -*-\n#\n# Dirigible documentation build configuration file, created by\n# sphinx-quickstart on Wed Oct 27 18:47:13 2010.\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.\n#sys.path.insert(0, os.path.abspath('.'))\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']\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.\nsource_encoding = 'utf-8-sig'\n\n# The master toctree document.\nmaster_doc = 'index'\n\n# General information about the project.\nproject = u'Dirigible'\ncopyright = u''\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 = '0.9'\n# The full version, including alpha/beta/rc tags.\nrelease = '0.9'\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 = 'default'\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 = 'dirigible-theme'\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 = ['.']\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\".\n# html_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 = 'Dirigibledoc'\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', 'Dirigible.tex', u'Dirigible Documentation',\n   u'Resolver Systems Ltd', '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', 'dirigible', u'Dirigible Documentation',\n     [u'Resolver Systems Ltd'], 1)\n]\n"
  },
  {
    "path": "documentation/dirigible-theme/layout.html",
    "content": "{% extends \"default/layout.html\" %}\r\n\r\n"
  },
  {
    "path": "documentation/dirigible-theme/static/dirigible-style.css",
    "content": "@import \"default.css\";\r\n\r\n/* for the layout of table in 'spreadsheet-functions.html' */\r\n\r\ntd p {\r\n    margin: 0px;\r\n}\r\n.line-block {\r\n    margin: 0px;\r\n}\r\n\r\n\r\n/* for pygments code appearing in table in 'spreadsheet-functions.html' */\r\n\r\ntd div.highlight-python div.highlight pre {\r\n    margin: 2px;\r\n    padding: 4px;\r\n    border-style: none;\r\n}\r\n\r\ntd div.highlight-python pre {\r\n    margin: 2px;\r\n    padding: 4px;\r\n    border-style: none;\r\n}\r\n\r\nimg {\r\n    margin-left: 50px;\r\n}\r\n"
  },
  {
    "path": "documentation/dirigible-theme/theme.conf",
    "content": "[theme]\r\ninherit = default\r\nstylesheet = dirigible-style.css\r\n                 \r\n[options]\r\n\r\n"
  },
  {
    "path": "documentation/fl-python-differences.rst",
    "content": "Differences Between Dirigible's Formula Language and Python\r\n===========================================================\r\n\r\nWe call the syntax you use to enter formulae into the Dirigible spreadsheet grid the *formula language*.  It's almost exactly the same as Python expression syntax, but has some differences for compatibility with the formula syntax used in other spreadsheet applications.  (The Dirigible usercode, by contrast, is pure Python.)\r\n\r\nThis page highlights the differences between the formula language and normal Python.\r\n\r\n\"``:``\" operator replaced by \"``->``\"\r\n-------------------------------------\r\n\r\nThe colon (\"``:``\") is reserved for future use in cell ranges, e.g. ``C3:D8``.\r\n\r\nAll Python colons have been replaced by arrows (\"``->``\"). Lambdas, slices and dictionaries in the formula language are as follows:\r\n\r\n    ::\r\n\r\n        =lambda x -> x + 1\r\n        =somelist[3->6]\r\n        ={foo -> bar, baz -> qux}\r\n\r\n\r\nList comprehensions: ``in`` clause trailing commas disallowed\r\n-------------------------------------------------------------\r\n\r\nTrailing commas for the Python list comprehension ``in`` clause are disallowed. For example, ``=[a for a in 1, 2,]`` is invalid because of the last comma.\r\n"
  },
  {
    "path": "documentation/import_export.rst",
    "content": "Importing and Exporting\n=======================\n\nWe're keen to help you get data in and out of Dirigible. We currently support:\n\n * Import from Microsoft Excel (values only, no formulae yet)\n\n * Import from CSV (in Excel format or international format)\n\n * Export to CSV (Excel and international)\n\n\n\nImport from Excel\n^^^^^^^^^^^^^^^^^\n\n.. image:: import_export_import_icon.png\n\nYou can import Excel workbooks into Dirigible. Currently only values are\nimported and only Excel versions up to 2003 are supported. Charts, macros,\npictures, embedded objects, Visual Basic modules, formulae, comments and\nhyperlinks are ignored.\n\nTo import an Excel spreadsheet, click on the 'Import a file' button in the toolbar of a Dirigible sheet:\n\n.. image:: import_export_import_button.png\n\nYou will be presented with a dialog where you can select a file to import:\n\n.. image:: import_export_import_dialog.png\n\nWhen you select 'Upload Excel Values', you will be returned to your dashboard\npage. Each of the sheets in the Excel workbook (except empty ones) will be imported\nas a new Dirigible sheet and automatically added to your list of sheets. Their\nnames are based on the filename of the file you uploaded and the name of the\nsheet in the Excel workbook.\n\n\nDates and Times\n---------------\nDates and times in the Excel workbook will be converted to Dirigible DateTime\nobjects.\n\nThese behave exactly like Python `datetime.datetime\n<http://docs.python.org/library/datetime.html#datetime.datetime>`_ objects.\n\n\nImport from CSV\n^^^^^^^^^^^^^^^\n\n.. image:: import_export_import_icon.png\n\nCSV or \"comma-separated values\" is a popular format for encoding tabular data in a text file.\n\n    http://en.wikipedia.org/wiki/Comma-separated_values\n\nIf you're looking to get data in from Excel, you may want to take a look at our\nImport from Excel option first - aside from anything else, it can handle doing\nmore than one sheet at a time. If you're looking to import data from a database\nor similar, you'll probably find there's an option to export to CSV, and you'll\nthen be able to import it into Dirigible.\n\nYou'll find the import icon on the tool bar of a Dirigible sheet.  Select a CSV\nfile from your PC, and you'll get the option to upload it.  The data will be\npasted into your sheet at the current selected cell position (this lets you\nimport new data inside of a sheet you've already started work on but\n**be warned**, it will overwrite any data that's below and to the right of your\ncurrent cursor position.\n\n.. image:: import_export_import_csv_options.jpg\n\nA brief word on the difference between \"Excel-encoded\" and \"international\"\nCSVs.  (Here, we're talking about the way text characters are encoded\nin the file, not the syntax of how to delimit fields and quoting.\n\n\nExcel-encoded CSVs\n------------------\n\nExcel uses the Windows-1252_ encoding, which, despite being predicatably\nincompatible with ISO standards, does at least handle most western\neuropean characters.  This is our default setting for imports, and you'll\nprobably find that, due to the popularity of Excel, most CSVs you find lying\naround will be encoded in this format.  We recommend you try this first.\n\n\nOther encodings\n---------------\n\nIf you've tried importing your CSV in Excel format and you found some of\nyour characters are garbled, then you've probably got a CSV file that's encoded\naccording to a different standard, like unicode_. This might be because it\ncontains some non-western characters, or perhaps because it was produced by\npeople who actually follow standards...  Either\nway, there's a good chance you can still import it into Dirigible using our\n**auto-detect** mode, which is based on chardet_.  Give it a try, but be warned\nthat if your file *was* encoded in Excel format, auto-detect will get it wrong,\nso always try Excel first.\n\n.. _Windows-1252: http://en.wikipedia.org/wiki/Windows-1252\n.. _chardet: http://chardet.feedparser.org/\n.. _unicode: http://chardet.feedparser.org/\n\n\nExport to CSV\n-------------\n\n.. image:: import_export_export_icon.png\n\nIf you want to get your data out of Dirigible and into a desktop application, you'll\nprobably find there's an option to import data from CSV in it.  Industry\nleading spreadsheet applications such as Microsoft Excel\ncertainly offer you the option of opening CSV files\n\n    http://en.wikipedia.org/wiki/Comma-separated_values\n\nThe dirigible Export menu gives you two options:\n\nDownload for Excel (default)\n----------------------------\n\nTry this option first - it will produce a Windows-1252_ encoded\nCSV file which Excel, and any other applications that are likely to want to be\ncompatible with Excel, can read.\n\n\nDownload as international (utf8-encoded) CSV\n--------------------------------------------\n\nTry this if you run into any problems.  UTF-8 is a flavour of unicode_, and\nwithout going into the gory details, it should be able to handle pretty much\nany kind of characters. However if you try and import a UTF-8 encoded file into\nExcel, you'll probably find Excel gets it all wrong.  But don't despair,\nthere's a workaround:\n\n.. _resolver_one:\n\n\nHelp! my CSV file is all garbled when I open it in Excel!\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n.. image:: import_export_excel_garbled_csv.png\n\nThe problem you've run into is that Excel automatically assumes that CSV files\nare encoded with Microsoft's unilaterally imposed Windows-1252_ encoding, which\ncan't handle half the world's characters.  But don't despair, Excel does have a\nworkaround, the `Text import wizard\n<http://office.microsoft.com/en-us/excel-help/text-import-wizard-HP010102244.aspx>`_\n\n- Find the text import wizard via **Data --> Get External Data**\n\n.. image:: import_export_import_excel_data_import_from_text_button.png\n\n- Select **Delimited**\n- Under **File Origin**, choose **65001: Unicode (UTF-8)**\n\n.. image:: import_export_excel_import_text_menu.png\n\n- Cick **Next**\n- Tick the **comma** character\n\n.. image:: import_export_excel_import_text_choose_comma.png\n\nAnd you're good to go!\n\n.. image:: import_export_excel_hooray.png\n\n(We love Excel really)\n\n"
  },
  {
    "path": "documentation/index.rst",
    "content": "\nDirigible documentation contents\n================================\n\nGetting started\n---------------\n\n.. toctree::\n    :maxdepth: 1\n\n    tutorial01.rst\n    tutorial02.rst\n    tutorial03.rst\n    tutorial04.rst\n\n\nReference\n---------\n\n.. toctree::\n    :maxdepth: 1\n\n    overview.rst\n    builtins.rst\n    spreadsheet-functions.rst\n    fl-python-differences.rst\n    python-modules.rst\n    public_sheets.rst\n    json_api.rst\n    import_export.rst\n\n"
  },
  {
    "path": "documentation/json_api.rst",
    "content": "The JSON API\n============\n\nA JSON API is a URL which sends back a chunk of textual data in the `JSON\nformat <http://www.json.org/>`_. These can be used by third party applications,\nincluding scripts you write yourself, to access and manipulate your Dirigible\nsheets over the internet.\n\nAccessing Dirigible Sheets\n--------------------------\n\nEach sheet can be accessed as JSON, in either a GET or POST request. The URL\nto retrieve a sheet's cell values is::\n\n    http://SHEET_URL/v0.1/json/?api_key=API_KEY\n\nWhere SHEET_URL is the URL of the sheet on the dirigible installation, i.e::\n\n    localhost:8000/user/USERNAME/sheet/SHEET_ID\n\nTo enable a sheet's JSON access and find out your sheet's API_KEY, open the\nsheet page in a browser and click the sheet's security settings button, in the\ntoolbar above the usercode editor.\n\n.. image:: security-dialog-button.png\n\nThis will display a dialog:\n\n.. image:: security-dialog-json.png\n\nClick the appropriate checkbox to enable JSON access. A random API key will be\ngenerated for the sheet. You may change this if you wish, for example to deny\naccess to third parties who have discovered the API key for this sheet.\n\nWhen sending requests for the sheet content, pass the API key as a\nparameter (each sheet has a different API key)::\n\n    http://SHEET_URL/v0.1/json?api-key=API_KEY\n\nThis is the URL that is provided in the 'Sheet API URL' field in the dialog.\n\n\nOverriding Cell Values\n----------------------\n\nAs well as retrieving cell values, the JSON API can override the formulae of\ncells.\n\nThis allows you to set up a sheet to perform a calculation, and then have\nan application use the JSON API to provide alternative input values and\nretrieve the new results after the sheet is recalculated.\n\nPass the cell overrides as either GET or POST parameters::\n\n    http://SHEET_URL/v0.1/json?CELL=FORMULA\n\nCELL\n    specifies a cell location, in any of the formats 'A2', 'a2', or '1,2'.\n\nFORMULA\n    is any value you might enter as a cell formula, such as '42'::\n\n        http://SHEET_URL/v0.1/json?A2=42\n\n    Or you may pass a cell formula, starting with an equals. These\n    formulae, like all GET and POST parameters, need to be URL\n    encoded. For example, setting cell B3 to '=c2+2' in a GET request\n    ends up looking like:\n\n        http://SHEET_URL/v0.1/json?b3=%3DC1%2B2\n\n    %3D is '='; %2B is '+'\n\nSeveral cell overrides may be composed in a single request, eg::\n\n    http://SHEET_URL/v0.1/json?a2=123&b3=456\n\nThese overrides are transient and local to the request. No other requests\nfor the sheet will see their effects.\n\n"
  },
  {
    "path": "documentation/overview.rst",
    "content": "Technical overview\r\n==================\r\n\r\nThis page explains the details of how your Dirigible models are recalculated.  It assumes you're familiar with the basics of what Dirigible is and does.  For a more gentle introduction, try the :doc:`tutorial01`.\r\n\r\nWhen you editing a Dirigible model, you can put **formulae** and **constant values** into a spreadsheet grid, and you can edit Python **usercode** over on the right.  Every time you change something in the grid or modify the usercode, the model is recalculated and -- perhaps after a pause -- the updated values will appear.  This page explains exactly what goes on under the hood when that happens.\r\n\r\nTo recalculate the model, the server simply runs its usercode in a Python context containing a few \"magic\" functions and variables.  The data you've entered into the spreadsheet is not touched unless you have usercode that explicitly looks at it.  The default usercode is set up to manipulate the spreadsheet data so that it is processed like it would be in a traditional spreadsheet, but you could quite easily write usercode to process it in a completely different manner -- or to ignore it and simply use it as an input into your own code.\r\n\r\nThe three \"magic\" things that make the default behaviour possible are:\r\n\r\n- The :const:`worksheet` object\r\n- The :func:`load_constants` function\r\n- The :func:`evaluate_formulae` function\r\n\r\nThey are described in detail on this page: :doc:`builtins`.\r\n"
  },
  {
    "path": "documentation/public_sheets.rst",
    "content": "Making sheets public\n====================\n\nDirigible allows you to make any of your sheets public.  A public sheet can be seen and copied\n-- but not changed -- by anyone, including people who aren't logged in to Dirigible,\nso it's a great way to share your work without worrying about other people breaking things.\n\nTo make a sheet public, go to the sheet page's and click the security settings button, in the\ntoolbar above the usercode editor.\n\n.. image:: security-dialog-button.png\n\nThis will display a dialog:\n\n.. image:: security-dialog-public.png\n\nCheck the \"Make sheet public\", and click OK.\n\nOther people use the same URL to view your sheets as you do to view and change them,\nso if you want to send someone a link, just use the contents of your browser's address bar.\n"
  },
  {
    "path": "documentation/python-modules.rst",
    "content": "Batteries included: Python modules you can use\r\n==============================================\r\n\r\nDirigible formulae and usercode are written using Python, version 2.5.\r\n\r\nThe following Python libraries are supported:\r\n\r\n* All of the built-in modules supplied with `Python 2.5 <http://docs.python.org/2.5>`_\r\n* `NumPy <http://numpy.scipy.org/>`_, the fundamental package needed for scientific computing with Python.\r\n* `GmPy <http://gmpy.sourceforge.net/>`_, provides multiprecision arithmetic\r\n* `MpMath <http://code.google.com/p/mpmath/>`_, another library for multiprecision floating-point arithmetic.\r\n* The `SciPy <http://www.scipy.org/>`_ library, providing scientific tools for mathematics, science, and engineering.\r\n* `PyCrypto <http://www.dlitz.net/software/pycrypto/>`_, the Python cryptography toolkit.\r\n* `SqlAlchemy <http://www.sqlalchemy.org/>`_, a Python SQL toolkit and object\r\n  relational mapper.\r\n* `LXML <http://codespeak.net/lxml/>`_, for working with XML and HTML.\r\n* `xlrd <http://pypi.python.org/pypi/xlrd>`_, for reading Excel files.\r\n* `RDFLib <http://www.rdflib.net/>`_, for working with `RDF <http://www.w3.org/RDF/>`_.\r\n* `GeoPy <http://code.google.com/p/geopy/>`_, a geocoding toolbox.\r\n* `Beautiful Soup <http://www.crummy.com/software/BeautifulSoup/>`_, a forgiving\r\n  HTML parser for real-world web pages.\r\n* `Mechanize <http://wwwsearch.sourceforge.net/mechanize/>`_, stateful\r\n  programmatic web browsing.\r\n\r\n"
  },
  {
    "path": "documentation/spreadsheet-functions.rsl",
    "content": "<!--\r\n\r\n-->\r\n<document currentResultWorksheet=\"main table\" verticalSplitterRatio=\"0.80390625\" horizontalSplitterRatio=\"0.734640522876\" splitterOrientation=\"Horizontal\" suspendRecalculations=\"True\" license=\"Personal non-commercial use\" copyrightOwner=\"None\" resolverVersion=\"1.8\" resolverBuild=\"5104\" documentVersion=\"13\">\r\n    <foldState license=\"True\" imports=\"True\" worksheetCreation=\"True\" preConstantsUserCode=\"False\" constants=\"True\" preFormulaeUserCode=\"False\" generated=\"True\" postFormulaeUserCode=\"False\"/>\r\n    <traitDefaults>\r\n\r\n    </traitDefaults>\r\n    <preConstantsUserCode><![CDATA[from itertools import izip_longest\r\nfrom os.path import dirname, join\r\nimport time\r\nfrom urllib import urlopen\r\n\r\nfrom BeautifulSoup import BeautifulSoup\r\n\r\nmicrosoft_excel_functions_www_page = 'http://office.microsoft.com/en-us/excel-help/list-of-worksheet-functions-by-category-HP005204211.aspx'\r\n\r\n\r\nclass FunctionDef(object):\r\n    def __repr__(self):\r\n        return '<def %s>' % (self.excel_function_name,)\r\n    \r\n    @property\r\n    def category(self):\r\n        getcat = lambda r: mt.Cols['category'][r] and mt.Cols['category'][r] or getcat(r-1)\r\n        return getcat(self._row)\r\n\r\n\r\n]]></preConstantsUserCode>\r\n    <preFormulaeUserCode><![CDATA[mt = workbook['main table']\r\n\r\n    \r\ndef fetch_syntaxes_from_web():\r\n    main_doc_page = BeautifulSoup(urlopen(microsoft_excel_functions_www_page).read())\r\n    all_links = main_doc_page.findAll('a', href=True)\r\n    \r\n    def find_link_for_formula(formula_name):\r\n        for link in all_links:\r\n            if link.string == formula_name:\r\n                return link['href']\r\n\r\n    def get_syntax_from_page(href, formula_name):\r\n        try:\r\n            page = BeautifulSoup(urlopen(href).read())\r\n            elements = page.find('div',{'class':'cdArticleBody'}).find('b',text=formula_name).parent.parent.contents\r\n            return ''.join(e.string for e in elements)\r\n        except:\r\n            return ''\r\n\r\n    def get_syntax(name, formula_page):\r\n        print name,\r\n        syntax = get_syntax_from_page(formula_page, cell.Value)\r\n        retries = 3\r\n        while not syntax and retries > 0:\r\n            time.sleep(1)\r\n            retries = retries - 1\r\n            print retries,\r\n        print syntax\r\n        return syntax\r\n    \r\n    for cell in mt.Cols['function'].Cells:\r\n        if cell.Value and cell.Row > 1:\r\n            formula_page = find_link_for_formula(cell.Value)\r\n            mt.Cells[cell.Col, cell.Row].Href = formula_page\r\n            syntax = get_syntax(cell.Value, formula_page)\r\n            if syntax:\r\n                mt.Cells[cell.Col+1, cell.Row].Formula = syntax\r\n\r\nb = Button('Fetch Syntaxes from web')\r\nb.Click += fetch_syntaxes_from_web\r\nmt.C2 = b\r\n            \r\n]]></preFormulaeUserCode>\r\n    <names />\r\n    <viewStates>\r\n        <viewState worksheet=\"main table\">\r\n            <currentCell col=\"3\" row=\"345\" />\r\n            <scrollPos col=\"3\" row=\"2\" />\r\n            <virtualGridSize width=\"23\" height=\"383\" />\r\n        </viewState>\r\n    </viewStates>\r\n    <worksheetOrder>\r\n        <orderentry name=\"main table\" />\r\n    </worksheetOrder>\r\n    <worksheet name=\"main table\" value=\"\" showGrid=\"True\" showBounds=\"False\" headerWidth=\"-1\" headerHeight=\"-1\" headerRow=\"1\" freezeRows=\"1\" freezeCols=\"2\">\r\n        <trait name=\"FontSize\">\r\n            <traitentry col=\"0\" row=\"0\" value=\"11.0\" />\r\n        </trait>\r\n        <trait name=\"BorderBottom\">\r\n        </trait>\r\n        <trait name=\"ShowNegativeSymbol\">\r\n        </trait>\r\n        <trait name=\"Unit\">\r\n        </trait>\r\n        <trait name=\"ColWidth\">\r\n            <traitentry col=\"2\" row=\"0\" value=\"114\" />\r\n            <traitentry col=\"3\" row=\"0\" value=\"347\" />\r\n            <traitentry col=\"4\" row=\"0\" value=\"247\" />\r\n            <traitentry col=\"5\" row=\"0\" value=\"179\" />\r\n            <traitentry col=\"6\" row=\"0\" value=\"124\" />\r\n            <traitentry col=\"7\" row=\"0\" value=\"214\" />\r\n            <traitentry col=\"7\" row=\"346\" value=\"179\" />\r\n            <traitentry col=\"7\" row=\"360\" value=\"179\" />\r\n        </trait>\r\n        <trait name=\"Alignment\">\r\n        </trait>\r\n        <trait name=\"BorderTop\">\r\n        </trait>\r\n        <trait name=\"WebEditable\">\r\n        </trait>\r\n        <trait name=\"Italic\">\r\n        </trait>\r\n        <trait name=\"FontFamily\">\r\n            <traitentry col=\"0\" row=\"0\" value=\"Calibri\" />\r\n        </trait>\r\n        <trait name=\"ShowSeparators\">\r\n        </trait>\r\n        <trait name=\"VerticalAlignment\">\r\n        </trait>\r\n        <trait name=\"Formatter\">\r\n        </trait>\r\n        <trait name=\"Underline\">\r\n        </trait>\r\n        <trait name=\"Color\">\r\n            <traitentry col=\"0\" row=\"0\" value=\"ARGB color: 255, 0, 0, 0\" />\r\n        </trait>\r\n        <trait name=\"RowHidden\">\r\n        </trait>\r\n        <trait name=\"BorderLeft\">\r\n        </trait>\r\n        <trait name=\"StripZeros\">\r\n        </trait>\r\n        <trait name=\"ColHidden\">\r\n        </trait>\r\n        <trait name=\"Wrap\">\r\n            <traitentry col=\"4\" row=\"0\" value=\"True\" />\r\n        </trait>\r\n        <trait name=\"NegativeTextColor\">\r\n            <traitentry col=\"0\" row=\"0\" value=\"named color: Black\" />\r\n        </trait>\r\n        <trait name=\"BorderRight\">\r\n        </trait>\r\n        <trait name=\"RowHeight\">\r\n            <traitentry col=\"0\" row=\"0\" value=\"20\" />\r\n            <traitentry col=\"0\" row=\"190\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"2\" value=\"60\" />\r\n            <traitentry col=\"0\" row=\"11\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"12\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"13\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"202\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"208\" value=\"60\" />\r\n            <traitentry col=\"0\" row=\"22\" value=\"103\" />\r\n            <traitentry col=\"0\" row=\"24\" value=\"120\" />\r\n            <traitentry col=\"0\" row=\"26\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"216\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"221\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"222\" value=\"60\" />\r\n            <traitentry col=\"0\" row=\"223\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"37\" value=\"136\" />\r\n            <traitentry col=\"0\" row=\"41\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"230\" value=\"140\" />\r\n            <traitentry col=\"0\" row=\"232\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"46\" value=\"131\" />\r\n            <traitentry col=\"0\" row=\"238\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"239\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"240\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"241\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"242\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"243\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"244\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"56\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"245\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"246\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"58\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"59\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"60\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"249\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"251\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"252\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"253\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"66\" value=\"112\" />\r\n            <traitentry col=\"0\" row=\"255\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"67\" value=\"52\" />\r\n            <traitentry col=\"0\" row=\"256\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"257\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"69\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"258\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"70\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"71\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"260\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"261\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"262\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"263\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"265\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"266\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"267\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"268\" value=\"60\" />\r\n            <traitentry col=\"0\" row=\"82\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"83\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"272\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"85\" value=\"58\" />\r\n            <traitentry col=\"0\" row=\"279\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"280\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"97\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"98\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"293\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"105\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"296\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"109\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"110\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"111\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"300\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"112\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"301\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"302\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"303\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"117\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"306\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"119\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"308\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"311\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"124\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"317\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"318\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"319\" value=\"60\" />\r\n            <traitentry col=\"0\" row=\"131\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"320\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"132\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"133\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"134\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"135\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"136\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"325\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"137\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"138\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"327\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"139\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"140\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"329\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"141\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"142\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"143\" value=\"60\" />\r\n            <traitentry col=\"0\" row=\"144\" value=\"60\" />\r\n            <traitentry col=\"0\" row=\"146\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"147\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"336\" value=\"60\" />\r\n            <traitentry col=\"0\" row=\"148\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"337\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"338\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"151\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"340\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"152\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"341\" value=\"107\" />\r\n            <traitentry col=\"0\" row=\"153\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"342\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"154\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"155\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"344\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"156\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"345\" value=\"64\" />\r\n            <traitentry col=\"0\" row=\"157\" value=\"60\" />\r\n            <traitentry col=\"0\" row=\"346\" value=\"60\" />\r\n            <traitentry col=\"0\" row=\"160\" value=\"60\" />\r\n            <traitentry col=\"0\" row=\"161\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"350\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"162\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"351\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"163\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"352\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"164\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"166\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"167\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"356\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"168\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"169\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"172\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"173\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"174\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"363\" value=\"63\" />\r\n            <traitentry col=\"0\" row=\"175\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"176\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"178\" value=\"60\" />\r\n            <traitentry col=\"0\" row=\"179\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"180\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"181\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"182\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"183\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"186\" value=\"40\" />\r\n            <traitentry col=\"0\" row=\"188\" value=\"40\" />\r\n            <traitentry col=\"7\" row=\"21\" value=\"40\" />\r\n            <traitentry col=\"7\" row=\"19\" value=\"40\" />\r\n            <traitentry col=\"7\" row=\"83\" value=\"20\" />\r\n            <traitentry col=\"6\" row=\"11\" value=\"20\" />\r\n            <traitentry col=\"6\" row=\"12\" value=\"20\" />\r\n            <traitentry col=\"6\" row=\"26\" value=\"20\" />\r\n            <traitentry col=\"6\" row=\"41\" value=\"20\" />\r\n        </trait>\r\n        <trait name=\"BackColor\">\r\n            <traitentry col=\"0\" row=\"0\" value=\"named color: White\" />\r\n            <traitentry col=\"3\" row=\"0\" value=\"ARGB color: 255, 255, 255, 128\" />\r\n        </trait>\r\n        <trait name=\"Bold\">\r\n        </trait>\r\n        <trait name=\"Strikethrough\">\r\n        </trait>\r\n        <trait name=\"ImagePlacement\">\r\n        </trait>\r\n        <trait name=\"DecimalPlaces\">\r\n        </trait>\r\n        <cell name=\"B194\" value=\"ISNA\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E7\" value=\"math.asin(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"F7\" value=\"import math\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D180\" value=\"Returns the net present value for a schedule of cash flows that is not necessarily periodic\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B5\" value=\"ACOS\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D321\" value=\"Returns the Student&apos;s t-distribution\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C4\" value=\"ABS(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B177\" value=\"TBILLYIELD\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C176\" value=\"TBILLPRICE(settlement,maturity,discount)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B324\" value=\"TRIMMEAN\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C325\" value=\"TTEST(array1,array2,tails,type)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D101\" value=\"Converts a decimal number to octal\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D306\" value=\"Returns the number of permutations for a given number of objects\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"G99\" value=\"bin(n) without the slice returns a string prepended with &apos;0b&apos;, whereas excel just returns raw binary.\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D137\" value=\"Returns the number of days from the settlement date to the next coupon date\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B96\" value=\"BIN2OCT\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C97\" value=\"COMPLEX(real_num,i_num,suffix)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B311\" value=\"RSQ\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C310\" value=\"RANK(number,ref,order)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B140\" value=\"COUPPCD\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C141\" value=\"CUMIPMT(rate,nper,pv,start_period,end_period,type)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D122\" value=\"Returns the sine of a complex number\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D279\" value=\"Returns the inverse of the gamma cumulative distribution\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D238\" value=\"Extracts from a database a single record that matches the specified criteria\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B127\" value=\"OCT2DEC\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C126\" value=\"OCT2BIN(number,places)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B274\" value=\"FISHERINV\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C275\" value=\"FORECAST(x,known_y&apos;s,known_x&apos;s)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D47\" value=\"Returns the sign of a number\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B235\" value=\"DAVERAGE\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E40\" value=\"random.random()\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"F40\" value=\"import random\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"G41\" value=\"Note minor difference between randint and randrange, &lt;bottom and &lt;=bottom\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B42\" value=\"ROMAN\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C43\" value=\"ROUND(number,num_digits)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D95\" value=\"Converts a binary number to hexadecimal\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D195\" value=\"Returns TRUE if the value is not text\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B90\" value=\"BESSELI\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C91\" value=\"BESSELJ(x,n)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D12\" value=\"Rounds a number to the nearest integer or to the nearest multiple of significance\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B198\" value=\"ISREF\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E11\" value=\"math.atanh(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"F11\" value=\"import math\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D176\" value=\"Returns the price per $100 face value for a Treasury bill\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B9\" value=\"ATAN\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C8\" value=\"ASINH(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D329\" value=\"Calculates variance based on the entire population, including numbers, text, and logical values\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B181\" value=\"YIELD\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C180\" value=\"XNPV(rate,values,dates)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B328\" value=\"VARP\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C329\" value=\"VARPA(value1,value2,...)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D97\" value=\"Converts real and imaginary coefficients into a complex number\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E102\" value=\"number1 == number2\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D318\" value=\"Calculates standard deviation based on the entire population\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"G103\" value=\"NB. scipy version takes complex number as only parameter. not equivalent\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"G312\" value=\"See scipy.stats.skew\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D149\" value=\"Returns the effective annual interest rate\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B100\" value=\"DEC2HEX\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C101\" value=\"DEC2OCT(number, places)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B315\" value=\"STANDARDIZE\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C314\" value=\"SMALL(array,k)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B144\" value=\"DDB\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C145\" value=\"DISC(settlement,maturity,pr,redemption,basis)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D70\" value=\"the serial number of the date that is the indicated number of months before or after the start date\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D275\" value=\"Returns a value along a linear trend\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B67\" value=\"DATEVALUE\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C66\" value=\"DATE(year,month,day)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B278\" value=\"GAMMADIST\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C279\" value=\"GAMMAINV(probability,alpha,beta)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D43\" value=\"Rounds a number to a specified number of digits\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B239\" value=\"DMAX\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C238\" value=\"DGET(database,field,criteria)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B46\" value=\"SERIESSUM\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C47\" value=\"SIGN(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D91\" value=\"Returns the Bessel function Jn(x)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E92\" value=\"special.kn(n,x)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"F92\" value=\"from scipy import special\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"G93\" value=\"Note arg order is swapped\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D207\" value=\"Specifies a logical test to perform\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B94\" value=\"BIN2DEC\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E200\" value=\"float(value)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C95\" value=\"BIN2HEX(number,places)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D8\" value=\"Returns the inverse hyperbolic sine of a number\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B202\" value=\"TYPE\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E15\" value=\"math.cosh(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D293\" value=\"Returns the maximum value in a list of arguments, including numbers, text, and logical values\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"F15\" value=\"import math\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D188\" value=\"Returns information about the current operating environment\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B13\" value=\"COMBIN\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B288\" value=\"LINEST\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C12\" value=\"CEILING(number,significance)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C289\" value=\"LOGEST(known_y&apos;s,known_x&apos;s,const,stats)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D341\" value=\"Joins several text items into one text item\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"A185\" value=\"Information functions\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E338\" value=\"chr(i)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B189\" value=\"ISBLANK\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C188\" value=\"INFO(type_text)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B336\" value=\"ASC\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C337\" value=\"BAHTTEXT(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D109\" value=\"Returns the absolute value (modulus) of a complex number\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D314\" value=\"Returns the k-th smallest value in a data set\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D145\" value=\"Returns the discount rate for a security\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B104\" value=\"ERFC\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C105\" value=\"GESTEP(number,step)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B319\" value=\"STDEVPA\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C318\" value=\"STDEVP(number1,number2,...)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B148\" value=\"DURATION\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C149\" value=\"EFFECT(nominal_rate,npery)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D66\" value=\"the serial number of a particular date\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D287\" value=\"Returns the k-th largest value in a data set\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D246\" value=\"Calculates variance based on the entire population of selected database entries\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B71\" value=\"EOMONTH\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C70\" value=\"EDATE(start_date,months)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B282\" value=\"GROWTH\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C283\" value=\"HARMEAN(number1,number2,...)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D55\" value=\"Adds the cells in a range that meet multiple criteria\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B243\" value=\"DSTDEVP\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E48\" value=\"math.sin(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C242\" value=\"DSTDEV(database,field,criteria)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"F48\" value=\"import math\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D51\" value=\"Returns the square root of (number * pi)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B50\" value=\"SQRT\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C51\" value=\"SQRTPI(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D167\" value=\"Returns the price per $100 face value of a security that pays periodic interest\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B54\" value=\"SUMIF\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B162\" value=\"ODDFYIELD\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C163\" value=\"ODDLPRICE(settlement,maturity,last_interest,rate,yld,redemption,frequency,basis)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D20\" value=\"Returns the double factorial of a number\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B206\" value=\"=&apos;FALSE&apos;\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E19\" value=\"math.factorial(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D289\" value=\"Returns the parameters of an exponential trend\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C207\" value=\"IF(logical_test,value_if_true,value_if_false)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"F19\" value=\"import math\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B17\" value=\"EVEN\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C16\" value=\"DEGREES(angle)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B292\" value=\"MAX\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D132\" value=\"Returns the accrued interest for a security that pays interest at maturity\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C293\" value=\"MAXA(value1,value2,...)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D337\" value=\"Converts a number to text, using the (baht) currency format\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C128\" value=\"OCT2HEX(number,places)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B340\" value=\"CODE\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C341\" value=\"CONCATENATE (text1,text2,...)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D105\" value=\"Tests whether a number is greater than a threshold value\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E110\" value=\"inumber.imag\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D262\" value=\"Counts how many numbers are in the list of arguments\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D157\" value=\"Returns the internal rate of return where positive and negative cash flows are financed at different rates\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B108\" value=\"HEX2OCT\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C109\" value=\"IMABS(inumber)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B259\" value=\"CHITEST\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D153\" value=\"Returns the interest payment for an investment for a given period\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C258\" value=\"CHIINV(probability,degrees_freedom)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B152\" value=\"INTRATE\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C153\" value=\"IPMT(rate,per,nper,pv,fv,type)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B156\" value=\"MDURATION\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C157\" value=\"MIRR(values,finance_rate,reinvest_rate)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D78\" value=\"the serial number of a particular time\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D283\" value=\"Returns the harmonic mean\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D242\" value=\"Estimates the standard deviation based on a sample of selected database entries\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B75\" value=\"NETWORKDAYS\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B286\" value=\"KURT\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C74\" value=\"MONTH(serial_number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C287\" value=\"LARGE(array,k)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C246\" value=\"DVARP(database,field,criteria)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D63\" value=\"Truncates a number to an integer\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D163\" value=\"Returns the price per $100 face value of a security with an odd last period\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B58\" value=\"SUMX2MY2\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C59\" value=\"SUMX2PY2(array_x,array_y)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B166\" value=\"PPMT\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C167\" value=\"PRICE(settlement,maturity,rate,yld,redemption,frequency,basis)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D16\" value=\"Converts radians to degrees\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B210\" value=\"OR\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E23\" value=\"int(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D301\" value=\"Returns the standard normal cumulative distribution\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D297\" value=\"Returns the most common value in a data set\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B21\" value=\"FLOOR\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C20\" value=\"FACTDOUBLE(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B296\" value=\"MINA\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D128\" value=\"Converts an octal number to hexadecimal\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C297\" value=\"MODE(number1,number2,...)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D349\" value=\"Returns the number of characters in a text string\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B300\" value=\"NORMINV\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C301\" value=\"NORMSDIST(z)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B133\" value=\"AMORDEGRC\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C132\" value=\"ACCRINTM(issue,settlement,rate,par,basis)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B344\" value=\"EXACT\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D117\" value=\"Returns the base-10 logarithm of a complex number\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D258\" value=\"Returns the inverse of the one-tailed probability of the chi-squared distribution\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B112\" value=\"IMCONJUGATE\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C113\" value=\"IMCOS(inumber)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B263\" value=\"COUNTA\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C262\" value=\"COUNT(value1,value2,...)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D229\" value=\"Returns the number of rows in a reference\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E226\" value=\"lookup_array.index( lookup_value )\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D38\" value=\"Returns the integer portion of a division\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B224\" value=\"INDIRECT\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C225\" value=\"LOOKUP(lookup_value,lookup_vector,result_vector)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D74\" value=\"a serial number to a month\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B35\" value=\"PI\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D359\" value=\"Converts its arguments to text\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C34\" value=\"ODD(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"G76\" value=\"Instead of a serial number, returns a datetime object, with properties such as &apos;.hour&apos;.\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D254\" value=\"Returns the beta cumulative distribution function\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B79\" value=\"TIMEVALUE\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C78\" value=\"TIME(hour,minute,second)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B354\" value=\"REPLACE, REPLACEB\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C355\" value=\"REPT(text,number_times)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B251\" value=\"AVERAGEA\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C250\" value=\"AVERAGE(number1,number2,...)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D59\" value=\"Returns the sum of the sum of squares of corresponding values in two arrays\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D175\" value=\"Returns the bond-equivalent yield for a Treasury bill\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B62\" value=\"TANH\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D324\" value=\"Returns the mean of the interior of a data set\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C63\" value=\"TRUNC(number,num_digits)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D211\" value=\"Returns the logical value TRUE\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B170\" value=\"PV\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C171\" value=\"RATE(nper,pmt,pv,fv,type,guess)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B321\" value=\"TDIST\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C320\" value=\"STEYX(known_y&apos;s,known_x&apos;s)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D28\" value=\"Returns the matrix determinant of an array\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E27\" value=\"math.log10(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"F27\" value=\"import math\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D24\" value=\"Returns the least common multiple\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E31\" value=\"number % divisor\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D309\" value=\"Returns the quartile of a data set\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E306\" value=\"len( itertools.permutations( my_list, r ) )\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B25\" value=\"LN\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C24\" value=\"LCM(number1,number2, ...)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D140\" value=\"Returns the previous coupon date before the settlement date\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B29\" value=\"MINVERSE\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D345\" value=\"Finds one text value within another (case-sensitive)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E350\" value=\"text.lower()\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C28\" value=\"MDETERM(array)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B304\" value=\"PERCENTILE\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C305\" value=\"PERCENTRANK(array,x,significance)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B137\" value=\"COUPDAYSNC\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C136\" value=\"COUPDAYS(settlement,maturity,frequency,basis)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B348\" value=\"LEFT, LEFTB\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D113\" value=\"Returns the cosine of a complex number\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D270\" value=\"Returns the exponential distribution\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B116\" value=\"IMLN\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C117\" value=\"IMLOG10(inumber)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B267\" value=\"COVAR\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D225\" value=\"Looks up values in a vector or array\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D34\" value=\"Rounds a number up to the nearest odd integer\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B228\" value=\"ROW\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E37\" value=\"# for two numbers\nnumber1 * number2\n \n# for more than two\nimport operator\nreduce(operator.mul, [number1, number2, number3...])\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C229\" value=\"ROWS(array)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B39\" value=\"RADIANS\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E81\" value=\"date.isoweekday()\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D355\" value=\"Repeats text a given number of times\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"H248\" value=\"see http://docs.scipy.org/doc/scipy/reference/tutorial/stats.html\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E356\" value=\"text[-length:]\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C38\" value=\"QUOTIENT(numerator,denominator)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"G80\" value=\"Instead of a serial number, returns a date object, with properties such as &apos;.year&apos;.\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D250\" value=\"Returns the average of its arguments\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B83\" value=\"WORKDAY\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C82\" value=\"WEEKNUM(serial_num,return_type)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B358\" value=\"SUBSTITUTE\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D198\" value=\"Returns TRUE if the value is a reference\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C359\" value=\"T(value)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E193\" value=\"isinstance(value, bool)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B255\" value=\"BETAINV\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C254\" value=\"BETADIST(x,alpha,beta,A,B)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D7\" value=\"Returns the arcsine of a number\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B195\" value=\"ISNONTEXT\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"G1\" value=\"comments\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D171\" value=\"Returns the interest rate per period of an annuity\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D320\" value=\"Returns the standard error of the predicted y-value for each x in the regression\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D223\" value=\"Uses an index to choose a value from a reference or array\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B174\" value=\"SYD\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C175\" value=\"TBILLEQ(settlement,maturity,discount)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B325\" value=\"TTEST\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C324\" value=\"TRIMMEAN(array,percent)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B218\" value=\"CHOOSE\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C219\" value=\"COLUMN(reference)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D100\" value=\"Converts a decimal number to hexadecimal\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E99\" value=\"bin( number )[2:].zfill( places )\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D305\" value=\"Returns the percentage rank of a value in a data set\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"G98\" value=\"scipy.constants contains several constants that may be used in unit conversion\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D136\" value=\"Returns the number of days in the coupon period that contains the settlement date\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B97\" value=\"COMPLEX\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C96\" value=\"BIN2OCT(number,places)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B308\" value=\"PROB\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C309\" value=\"QUARTILE(array,quart)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B141\" value=\"CUMIPMT\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C140\" value=\"COUPPCD(settlement,maturity,frequency,basis)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D125\" value=\"Returns the sum of complex numbers\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D266\" value=\"Counts the number of cells within a range that meet multiple criteria\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D121\" value=\"Returns the real coefficient of a complex number\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D278\" value=\"Returns the gamma distribution\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B120\" value=\"IMPRODUCT\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C121\" value=\"IMREAL(inumber)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B271\" value=\"FDIST\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C270\" value=\"EXPONDIST(x,lambda,cumulative)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D237\" value=\"Counts nonblank cells in a database\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B124\" value=\"IMSUB\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C125\" value=\"IMSUM(inumber1,inumber2,...)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B275\" value=\"FORECAST\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C274\" value=\"FISHERINV(y)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D46\" value=\"Returns the sum of a power series based on the formula\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B232\" value=\"VLOOKUP\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E41\" value=\"random.randint( bottom, top ) or random.randrange( bottom, top )\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"F41\" value=\"import random\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D82\" value=\"a serial number to a number representing where the week falls numerically with a year\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B43\" value=\"ROUND\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C42\" value=\"ROMAN(number,form)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B362\" value=\"UPPER\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D194\" value=\"Returns TRUE if the value is the #N/A error value\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C363\" value=\"VALUE(text)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E197\" value=\"number % 2 == 1\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"H1\" value=\"private comments\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B199\" value=\"ISTEXT\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E4\" value=\"abs(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D183\" value=\"Returns the annual yield of a security that pays interest at maturity\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B6\" value=\"ACOSH\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C7\" value=\"ASIN(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D219\" value=\"Returns the column number of a reference\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B178\" value=\"VDB\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C179\" value=\"XIRR(values,dates,guess)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B329\" value=\"VARPA\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C328\" value=\"VARP(number1,number2,...)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B222\" value=\"HYPERLINK\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C223\" value=\"INDEX(array,row_num,column_num)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D96\" value=\"Converts a binary number to octal\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E103\" value=\"special.erf(x)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D317\" value=\"Estimates standard deviation based on a sample, including numbers, text, and logical values\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"F103\" value=\"from scipy import special\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E314\" value=\"sorted( my_list )[k]\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"G102\" value=\"If you require the result to be 0 or 1, use  &apos;(number1 == number2) and 1 or 0&apos;\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D148\" value=\"Returns the annual duration of a security with periodic interest payments\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B101\" value=\"DEC2OCT\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C100\" value=\"DEC2HEX(number,places)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B312\" value=\"SKEW\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C313\" value=\"SLOPE(known_y&apos;s,known_x&apos;s)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B145\" value=\"DISC\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C144\" value=\"DDB(cost,salvage,life,period,factor)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D69\" value=\"the number of days between two dates based on a 360-day year\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E66\" value=\"datetime.datetime( yyyy, mm, dd)\n\nor::\n\ndatetime.date( yyyy, mm, dd )\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D274\" value=\"Returns the inverse of the Fisher transformation\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"F66\" value=\"import datetime\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B279\" value=\"GAMMAINV\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C278\" value=\"GAMMADIST(x,alpha,beta,cumulative)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D42\" value=\"Converts an arabic numeral to roman, as text\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B236\" value=\"DCOUNT\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C237\" value=\"DCOUNTA(database,field,criteria)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D94\" value=\"Converts a binary number to decimal\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B47\" value=\"SIGN\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D363\" value=\"Converts a text argument to a number\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C46\" value=\"SERIESSUM(x,n,m,coefficients)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B91\" value=\"BESSELJ\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C90\" value=\"BESSELI(x,n)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D206\" value=\"Returns the logical value FALSE\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D15\" value=\"Returns the hyperbolic cosine of a number\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E8\" value=\"math.asinh(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C202\" value=\"TYPE(value)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D292\" value=\"Returns the maximum value in a list of arguments\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"F8\" value=\"import math\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D179\" value=\"Returns the internal rate of return for a schedule of cash flows that is not necessarily periodic\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B10\" value=\"ATAN2\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D328\" value=\"Calculates variance based on the entire population\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C11\" value=\"ATANH(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B289\" value=\"LOGEST\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C288\" value=\"LINEST(known_y&apos;s,known_x&apos;s,const,stats)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B182\" value=\"YIELDDISC\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C183\" value=\"YIELDMAT(settlement,maturity,issue,rate,pr,basis)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D108\" value=\"Converts a hexadecimal number to octal\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D313\" value=\"Returns the slope of the linear regression line\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D144\" value=\"Returns the depreciation of an asset for a specified period by using the double-declining balance method or some other method that you specify\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B105\" value=\"GESTEP\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C104\" value=\"ERFC(x)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B316\" value=\"STDEV\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C317\" value=\"STDEVA(value1,value2,...)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B149\" value=\"EFFECT\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C148\" value=\"DURATION(settlement,maturity,coupon,yld,frequency,basis)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D286\" value=\"Returns the kurtosis of a data set\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D245\" value=\"Estimates variance based on a sample from selected database entries\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B68\" value=\"DAY\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C69\" value=\"DAYS360(start_date,end_date,method)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B283\" value=\"HARMEAN\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C282\" value=\"GROWTH(known_y&apos;s,known_x&apos;s,new_x&apos;s,const)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D54\" value=\"Adds the cells specified by a given criteria\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B240\" value=\"DMIN\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E49\" value=\"math.sinh(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C241\" value=\"DPRODUCT(database,field,criteria)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"F49\" value=\"import math\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D90\" value=\"Returns the modified Bessel function In(x)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B51\" value=\"SQRTPI\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E93\" value=\"special.yn(n,x)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"F93\" value=\"from scipy import special\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C50\" value=\"SQRT(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"G92\" value=\"Note arg order is swapped\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D166\" value=\"Returns the payment on the principal for an investment for a given period\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B95\" value=\"BIN2HEX\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C94\" value=\"BIN2DEC(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D202\" value=\"Returns a number indicating the data type of a value\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B163\" value=\"ODDLPRICE\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E205\" value=\"logical1 and logical2 and logical3...\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C162\" value=\"ODDFYIELD(settlement,maturity,issue,first_coupon,rate,pr,redemption,frequency,basis)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D11\" value=\"Returns the inverse hyperbolic tangent of a number\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B207\" value=\"IF\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E12\" value=\"math.ceil(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D288\" value=\"Returns the parameters of a linear trend\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C206\" value=\"FALSE( )\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"F12\" value=\"import math\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E295\" value=\"min(my_list)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D191\" value=\"Returns TRUE if the value is any error value\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B14\" value=\"COS\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D340\" value=\"Returns a numeric code for the first character in a text string\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C15\" value=\"COSH(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B293\" value=\"MAXA\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C292\" value=\"MAX(number1,number2,...)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B186\" value=\"CELL\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C187\" value=\"ERROR.TYPE(error_val)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B337\" value=\"BAHTTEXT\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C336\" value=\"ASC(text)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D104\" value=\"Returns the complementary error function\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D261\" value=\"Returns the correlation coefficient between two data sets\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D156\" value=\"Returns the Macauley modified duration for a security with an assumed par value of $100\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B109\" value=\"IMABS\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C108\" value=\"HEX2OCT(number,places)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B256\" value=\"BINOMDIST\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C257\" value=\"CHIDIST(x,degrees_freedom)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B153\" value=\"IPMT\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C152\" value=\"INTRATE(settlement,maturity,investment,redemption,basis)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"H79\" value=\"see http://docs.python.org/library/datetime.html#strftime-strptime-behavior\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D77\" value=\"a serial number to a second\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D282\" value=\"Returns values along an exponential trend\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"G75\" value=\"Implemented by this third-party package:http://pypi.python.org/pypi/workdays/\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D241\" value=\"Multiplies the values in a particular field of records that match the criteria in a database\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B72\" value=\"HOUR\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B287\" value=\"LARGE\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C73\" value=\"MINUTE(serial_number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C286\" value=\"KURT(number1,number2,...)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D50\" value=\"Returns a positive square root\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B244\" value=\"DSUM\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E53\" value=\"sum(list_of_numbers)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C245\" value=\"DVAR(database,field,criteria)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B55\" value=\"SUMIFS\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C54\" value=\"SUMIF(range,criteria,sum_range)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D162\" value=\"Returns the yield of a security with an odd first period\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B167\" value=\"PRICE\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E209\" value=\"not arg\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C166\" value=\"PPMT(rate,per,nper,pv,fv,type)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D23\" value=\"Rounds a number down to the nearest integer\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B211\" value=\"=&apos;TRUE&apos;\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E16\" value=\"math.degrees(angle)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D300\" value=\"Returns the inverse of the normal cumulative distribution\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C210\" value=\"OR(logical1,logical2,...)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"F16\" value=\"import math\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D187\" value=\"Returns a number corresponding to an error type\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B18\" value=\"EXP\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D336\" value=\"Changes full-width (double-byte) English letters or katakana within a character string to half-width (single-byte) characters\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C19\" value=\"FACT(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B297\" value=\"MODE\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C296\" value=\"MINA(value1,value2,...)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D348\" value=\"Returns the leftmost characters from a text value\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B190\" value=\"ISERR\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B341\" value=\"CONCATENATE\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"G346\" value=\"use string formatting\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C340\" value=\"CODE(text)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B345\" value=\"FIND, FINDB\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C344\" value=\"EXACT(text1,text2)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D116\" value=\"Returns the natural logarithm of a complex number\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D257\" value=\"Returns the one-tailed probability of the chi-squared distribution\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E262\" value=\"sum(1 for a in [value1, value2] if isinstance(a, int))\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D152\" value=\"Returns the interest rate for a fully invested security\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B113\" value=\"IMCOS\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C112\" value=\"IMCONJUGATE(inumber)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B260\" value=\"CONFIDENCE\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C261\" value=\"CORREL(array1,array2)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B157\" value=\"MIRR\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C156\" value=\"MDURATION(settlement,maturity,coupon,yld,frequency,basis)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D37\" value=\"Multiplies its arguments\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D73\" value=\"a serial number to a minute\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B32\" value=\"MROUND\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D358\" value=\"Substitutes new text for old text in a text string\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C33\" value=\"MULTINOMIAL(number1,number2, ...)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E353\" value=\"a_string.title()\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"G79\" value=\"Returns a datetime object\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D253\" value=\"Returns the average (arithmetic mean) of all cells that meet multiple criteria.\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B76\" value=\"NOW\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E250\" value=\"values = [number1, number2...]\naverage = sum(values) / len(values)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C77\" value=\"SECOND(serial_number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B355\" value=\"REPT\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"A248\" value=\"Statistical functions\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D62\" value=\"Returns the hyperbolic tangent of a number\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C249\" value=\"AVEDEV(number1,number2,...)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B59\" value=\"SUMX2PY2\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C58\" value=\"SUMX2MY2(array_x,array_y)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D174\" value=\"Returns the sum-of-years&apos; digits depreciation of an asset for a specified period\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D210\" value=\"Returns TRUE if any argument is TRUE\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B171\" value=\"RATE\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C170\" value=\"PV(rate,nper,pmt,fv,type)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"A215\" value=\"Lookup and reference functions\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D19\" value=\"Returns the factorial of a number\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E20\" value=\"scipy.misc.factorial2( number )\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D296\" value=\"Returns the smallest value in a list of arguments, including numbers, text, and logical values\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"F20\" value=\"import scipy\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"G21\" value=\"NB. does not handle the second &apos;significance&apos; argument\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D135\" value=\"Returns the number of days from the beginning of the coupon period to the settlement date\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B22\" value=\"GCD\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C23\" value=\"INT(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B301\" value=\"NORMSDIST\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D131\" value=\"Returns the accrued interest for a security that pays periodic interest\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C300\" value=\"NORMINV(probability,mean,standard_dev)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D344\" value=\"Checks to see if two text values are identical\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E351\" value=\"text[start:end]\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"A130\" value=\"Financial functions\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C131\" value=\"ACCRINT(issue,first_interest,settlement,rate,par,frequency,basis)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B134\" value=\"AMORLINC\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C135\" value=\"COUPDAYBS(settlement,maturity,frequency,basis)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B349\" value=\"LEN, LENB\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D112\" value=\"Returns the complex conjugate of a complex number\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D269\" value=\"Returns the sum of squares of deviations\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D228\" value=\"Returns the row number of a reference\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B117\" value=\"IMLOG10\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C116\" value=\"IMLN(inumber)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B264\" value=\"COUNTBLANK\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C265\" value=\"COUNTIF(range,criteria)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"G226\" value=\"Only handles match type &apos;equals&apos;\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B225\" value=\"LOOKUP\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C224\" value=\"INDIRECT(ref_text,a1)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D33\" value=\"Returns the multinomial of a set of numbers\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E38\" value=\"math.floor( numerator / denominator )\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"F38\" value=\"import math\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D85\" value=\"the year fraction representing the number of whole days between start_date and end_date\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B36\" value=\"POWER\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E82\" value=\"date.isocalendar()[1]\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D354\" value=\"Replaces characters within text\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C37\" value=\"PRODUCT(number1,number2,...)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E357\" value=\"text.lower().find( search_string.lower() )\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"G83\" value=\"Implemented by this third-party package: http://pypi.python.org/pypi/workdays/\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D249\" value=\"Returns the average of the absolute deviations of data points from their mean\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B80\" value=\"TODAY\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C81\" value=\"WEEKDAY(serial_number,return_type)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B359\" value=\"T\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C358\" value=\"SUBSTITUTE(text,old_text,new_text,instance_num)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D58\" value=\"Returns the sum of the difference of squares of corresponding values in two arrays\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B252\" value=\"AVERAGEIF\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E61\" value=\"math.tan(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"F61\" value=\"import math\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D6\" value=\"Returns the inverse hyperbolic cosine of a number\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E1\" value=\"howto in resolver\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"F1\" value=\"imports\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B63\" value=\"TRUNC\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C62\" value=\"TANH(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"A3\" value=\"Math and trigonometry functions\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D170\" value=\"Returns the present value of an investment\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D327\" value=\"Estimates variance based on a sample, including numbers, text, and logical values\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D222\" value=\"Creates a shortcut or jump that opens a document stored on a network server, an intranet, or the Internet\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B175\" value=\"TBILLEQ\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B322\" value=\"TINV\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C174\" value=\"SYD(cost,salvage,life,per)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C323\" value=\"TREND(known_y&apos;s,known_x&apos;s,new_x&apos;s,const)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D31\" value=\"Returns the remainder from division\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B219\" value=\"COLUMN\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E24\" value=\"def lcm(a, b):\n    return a * b // gcd(a, b)\n\ndef lcmm(*args):\n    return reduce(lcm, args)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C218\" value=\"CHOOSE(index_num,value1,value2,...)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D308\" value=\"Returns the probability that values in a range are between two limits\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B26\" value=\"LOG\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C27\" value=\"LOG10(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B305\" value=\"PERCENTRANK\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D143\" value=\"Returns the depreciation of an asset for a specified period by using the fixed-declining balance method\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C304\" value=\"PERCENTILE(array,k)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B138\" value=\"COUPNCD\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C139\" value=\"COUPNUM(settlement,maturity,frequency,basis)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D124\" value=\"Returns the difference between two complex numbers\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D265\" value=\"Counts the number of cells within a range that meet the given criteria\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D224\" value=\"Returns a reference indicated by a text value\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B121\" value=\"IMREAL\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C120\" value=\"IMPRODUCT(inumber1,inumber2,...)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B268\" value=\"CRITBINOM\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C269\" value=\"DEVSQ(number1,number2,...)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B229\" value=\"ROWS\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C228\" value=\"ROW(reference)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D45\" value=\"Rounds a number up, away from zero\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D81\" value=\"a serial number to a day of the week\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B40\" value=\"RAND\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C41\" value=\"RANDBETWEEN(bottom,top)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E361\" value=\"text.strip()\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"G360\" value=\"use string formatting\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D197\" value=\"Returns TRUE if the number is odd\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B84\" value=\"YEAR\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C85\" value=\"YEARFRAC(start_date,end_date,basis)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B363\" value=\"VALUE\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C362\" value=\"UPPER(text)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B192\" value=\"ISEVEN\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D2\" value=\"http://office.microsoft.com/en-us/excel-help/list-of-worksheet-functions-by-category-HP010079186.aspx\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E5\" value=\"math.acos(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"F5\" value=\"import math\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D182\" value=\"Returns the annual yield for a discounted security; for example, a Treasury bill\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B7\" value=\"ASIN\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D323\" value=\"Returns values along a linear trend\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C6\" value=\"ACOSH(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D218\" value=\"Chooses a value from a list of values\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B179\" value=\"XIRR\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B326\" value=\"VAR\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C178\" value=\"VDB(cost,salvage,life,start_period,end_period,factor,no_switch)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C327\" value=\"VARA(value1,value2,...)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B223\" value=\"INDEX\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D27\" value=\"Returns the base-10 logarithm of a number\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D304\" value=\"Returns the k-th percentile of values in a range\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C222\" value=\"HYPERLINK(link_location,friendly_name)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D316\" value=\"Estimates standard deviation based on a sample\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B30\" value=\"MMULT\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B309\" value=\"QUARTILE\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C31\" value=\"MOD(number,divisor)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C308\" value=\"PROB(x_range,prob_range,lower_limit,upper_limit)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D139\" value=\"Returns the number of coupons payable between the settlement date and maturity date\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B313\" value=\"SLOPE\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C312\" value=\"SKEW(number1,number2,...)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B142\" value=\"CUMPRINC\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C143\" value=\"DB(cost,salvage,life,period,month)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D120\" value=\"Returns the product of complex numbers\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D277\" value=\"Returns the result of an F-test\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D236\" value=\"Counts the cells that contain numbers in a database\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B125\" value=\"IMSUM\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B272\" value=\"FINV\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C124\" value=\"IMSUB(inumber1,inumber2)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C273\" value=\"FISHER(x)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D232\" value=\"Looks in the first column of an array and moves across the row to return the value of a cell\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C232\" value=\"VLOOKUP(lookup_value,table_array,col_index_num,range_lookup)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B237\" value=\"DCOUNTA\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D41\" value=\"Returns a random number between the numbers you specify\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C236\" value=\"DCOUNT(database,field,criteria)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E46\" value=\"sum(\n    c * x ** pwr\n    for c, pwd in zip(\n        coefficients,\n        range(n, len( coefficients ), m)\n    )\n)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D93\" value=\"Returns the Bessel function Yn(x)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B44\" value=\"ROUNDDOWN\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E90\" value=\"special.iv(n,x)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D362\" value=\"Converts text to uppercase\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C45\" value=\"ROUNDUP(number,num_digits)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"F90\" value=\"from scipy import special\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"G91\" value=\"Note arg order is swapped\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D193\" value=\"Returns TRUE if the value is a logical value\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B196\" value=\"ISNUMBER\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C197\" value=\"ISODD(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D14\" value=\"Returns the cosine of a number\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E9\" value=\"math.atan(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"F9\" value=\"import math\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D178\" value=\"Returns the depreciation of an asset for a specified or partial period by using a declining balance method\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B11\" value=\"ATANH\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C10\" value=\"ATAN2(x_num,y_num)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B183\" value=\"YIELDMAT\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B330\" value=\"WEIBULL\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C182\" value=\"YIELDDISC(settlement,maturity,pr,redemption,basis)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D103\" value=\"Returns the error function\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E96\" value=\"oct( int( my_binary_number_as_string, 2 ) )\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D99\" value=\"Converts a decimal number to binary\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"G97\" value=\"Does not handle &apos;suffix&apos; parameter, always uses &apos;j&apos;.\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D312\" value=\"Returns the skewness of a distribution\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E100\" value=\"hex( number )[2:].zfill( places ).upper()\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"G101\" value=\"oct(n) prepends with a 0\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B98\" value=\"CONVERT\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C99\" value=\"DEC2BIN(number,places)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D151\" value=\"Returns the future value of an initial principal after applying a series of compound interest rates\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B102\" value=\"DELTA\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B317\" value=\"STDEVA\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C103\" value=\"ERF(lower_limit,upper_limit)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C316\" value=\"STDEV(number1,number2,...)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B146\" value=\"DOLLARDE\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C147\" value=\"DOLLARFR(decimal_dollar,fraction)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D68\" value=\"a serial number to a day of the month\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D273\" value=\"Returns the Fisher transformation\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E67\" value=\"format = &quot;%d/%m/%Y&quot; # for example\ndatetime.datetime.strptime( date_text, format )\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"F67\" value=\"import datetime\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"G66\" value=\"NB. Neither datetime.datetime nor datetime.date add to integers to give a new date.\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"A65\" value=\"Date and time\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B276\" value=\"FREQUENCY\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C277\" value=\"FTEST(array1,array2)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D244\" value=\"Adds the numbers in the field column of records in the database that match the criteria\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B241\" value=\"DPRODUCT\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D53\" value=\"Adds its arguments\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C240\" value=\"DMIN(database,field,criteria)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E50\" value=\"math.sqrt(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"F50\" value=\"import math\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B48\" value=\"SIN\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E94\" value=\"int( my_binary_number_as_string, 2 )\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C49\" value=\"SINH(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"G95\" value=\"Returns a string\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D205\" value=\"Returns TRUE if all of its arguments are TRUE\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B92\" value=\"BESSELK\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E202\" value=\"type(value)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C93\" value=\"BESSELY(x,n)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B200\" value=\"N\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C201\" value=\"NA( )\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D10\" value=\"Returns the arctangent from x- and y-coordinates\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D295\" value=\"Returns the minimum value in a list of arguments\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E13\" value=\"len( itertools.product( range(number), repeat=number_chosen ) )\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"F13\" value=\"import itertools\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"G12\" value=\"NB. does not handle the second &apos;significance&apos; argument\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D190\" value=\"Returns TRUE if the value is any error value except #N/A\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D331\" value=\"Returns the one-tailed probability-value of a z-test\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B15\" value=\"COSH\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B290\" value=\"LOGINV\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C14\" value=\"COS(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C291\" value=\"LOGNORMDIST(x,mean,standard_dev)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B187\" value=\"ERROR.TYPE\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C186\" value=\"CELL(info_type,reference)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D111\" value=\"Returns the argument theta, an angle expressed in radians\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D260\" value=\"Returns the confidence interval for a population mean\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E104\" value=\"special.erfc(x)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"F104\" value=\"from scipy import special\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D147\" value=\"Converts a dollar price, expressed as a decimal number, into a dollar price, expressed as a fraction\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B106\" value=\"HEX2BIN\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B257\" value=\"CHIDIST\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C107\" value=\"HEX2DEC(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C256\" value=\"BINOMDIST(number_s,trials,probability_s,cumulative)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B150\" value=\"FV\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C151\" value=\"FVSCHEDULE(principal,schedule)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D285\" value=\"Returns the intercept of the linear regression line\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D281\" value=\"Returns the geometric mean\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B69\" value=\"DAYS360\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B280\" value=\"GAMMALN\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C68\" value=\"DAY(serial_number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C281\" value=\"GEOMEAN(number1,number2,...)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D240\" value=\"Returns the minimum value from selected database entries\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B284\" value=\"HYPGEOMDIST\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C285\" value=\"INTERCEPT(known_y&apos;s,known_x&apos;s)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B245\" value=\"DVAR\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D49\" value=\"Returns the hyperbolic sine of a number\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C244\" value=\"DSUM(database,field,criteria)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D165\" value=\"Returns the periodic payment for an annuity\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B52\" value=\"SUBTOTAL\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C53\" value=\"SUM(number1,number2, ...)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D201\" value=\"Returns the error value #N/A\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E206\" value=\"=&quot;False&quot;\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B160\" value=\"NPV\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C161\" value=\"ODDFPRICE(settlement,maturity,issue,first_coupon,rate,yld,redemption,frequency,basis)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"G207\" value=\"known as the &apos;and/or trick&apos;\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E210\" value=\"logical1 or logical2 or logical3...\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"A204\" value=\"Logical functions\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C205\" value=\"AND(logical1,logical2, ...)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B208\" value=\"IFERROR\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D22\" value=\"Returns the greatest common divisor\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D291\" value=\"Returns the cumulative lognormal distribution\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C209\" value=\"NOT(logical)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E17\" value=\"int(math.ceil(number / 2.0)) * 2\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E292\" value=\"max(my_list)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"F17\" value=\"import math\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D186\" value=\"Returns information about the formatting, location, or contents of a cell\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D343\" value=\"Converts a number to text, using the $ (dollar) currency format\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B19\" value=\"FACT\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B294\" value=\"MEDIAN\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C18\" value=\"EXP(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C295\" value=\"MIN(number1,number2,...)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B191\" value=\"ISERROR\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B338\" value=\"CHAR\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C339\" value=\"CLEAN(text)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D107\" value=\"Converts a hexadecimal number to decimal\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D256\" value=\"Returns the individual term binomial distribution probability\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E263\" value=\"len([value1, value2])\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D159\" value=\"Returns the number of periods for an investment\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B110\" value=\"IMAGINARY\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B261\" value=\"CORREL\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C111\" value=\"IMARGUMENT(inumber)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C260\" value=\"CONFIDENCE(alpha,standard_dev,size)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B154\" value=\"IRR\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D76\" value=\"the serial number of the current date and time\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D72\" value=\"a serial number to an hour\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D357\" value=\"Finds one text value within another (not case-sensitive)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E79\" value=\"datetime.strptime( time_text, python_date_string_format )\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E354\" value=\"text.replace( &apos;str_to_replace&apos;, &apos;str_to_replace_with&apos; )\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B73\" value=\"MINUTE\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C72\" value=\"HOUR(serial_number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D252\" value=\"Returns the average (arithmetic mean) of all the cells in a range that meet a given criteria\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B77\" value=\"SECOND\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B352\" value=\"PHONETIC\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C76\" value=\"NOW( )\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C353\" value=\"PROPER(text)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B249\" value=\"AVEDEV\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D61\" value=\"Returns the tangent of a number\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D161\" value=\"Returns the price per $100 face value of a security with an odd first period\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B56\" value=\"SUMPRODUCT\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C57\" value=\"SUMSQ(number1,number2, ...)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B164\" value=\"ODDLYIELD\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C165\" value=\"PMT(rate,nper,pv,fv,type)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D209\" value=\"Reverses the logic of its argument\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D18\" value=\"Returns e raised to the power of a given number\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D303\" value=\"Returns the Pearson product moment correlation coefficient\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E21\" value=\"math.floor(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E296\" value=\"min(my_list)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"F21\" value=\"import math\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"G297\" value=\"See scipy.stats.mode\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D134\" value=\"Returns the depreciation for each accounting period\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D339\" value=\"Removes all nonprintable characters from text\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B23\" value=\"INT\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B298\" value=\"NEGBINOMDIST\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C22\" value=\"GCD(number1,number2, ...)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C299\" value=\"NORMDIST(x,mean,standard_dev,cumulative)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B131\" value=\"ACCRINT\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C343\" value=\"DOLLAR(number,decimals)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D119\" value=\"Returns a complex number raised to an integer power\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D268\" value=\"Returns the smallest value for which the cumulative binomial distribution is less than or equal to a criterion value\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D155\" value=\"Calculates the interest paid during a specific period of an investment\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B114\" value=\"IMDIV\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B265\" value=\"COUNTIF\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C115\" value=\"IMEXP(inumber)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C264\" value=\"COUNTBLANK(range)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B158\" value=\"NOMINAL\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D36\" value=\"Returns the result of a number raised to a power\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C159\" value=\"NPER(rate, pmt, pv, fv, type)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E35\" value=\"math.pi\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"F35\" value=\"import math\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B33\" value=\"MULTINOMIAL\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C32\" value=\"MROUND(number,multiple)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D84\" value=\"a serial number to a year\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D353\" value=\"Capitalizes the first letter in each word of a text value\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E358\" value=\"text.replace( &apos;str_to_replace&apos;, &apos;str_to_replace_with&apos; )\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B81\" value=\"WEEKDAY\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B356\" value=\"RIGHT, RIGHTB\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C80\" value=\"TODAY( )\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B253\" value=\"AVERAGEIFS\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D57\" value=\"Returns the sum of the squares of the arguments\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E62\" value=\"math.tanh(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"F62\" value=\"import math\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"G63\" value=\"NB. Does not take second parameter.\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D173\" value=\"Returns the straight-line depreciation of an asset for one period\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B60\" value=\"SUMXMY2\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C61\" value=\"TAN(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D169\" value=\"Returns the price per $100 face value of a security that pays interest at maturity\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D326\" value=\"Estimates variance based on a sample\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B168\" value=\"PRICEDISC\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C169\" value=\"PRICEMAT(settlement,maturity,issue,rate,yld,basis)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D221\" value=\"Looks in the top row of an array and returns the value of the indicated cell\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E218\" value=\"[value1, value2...][index_num]\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B172\" value=\"RECEIVED\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B323\" value=\"TREND\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C173\" value=\"SLN(cost,salvage,life)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C322\" value=\"TINV(probability,degrees_freedom)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B216\" value=\"ADDRESS\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D30\" value=\"Returns the matrix product of two arrays\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D299\" value=\"Returns the normal cumulative distribution\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C217\" value=\"AREAS(reference)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E25\" value=\"math.log(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"F25\" value=\"import math\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D351\" value=\"Returns a specific number of characters from a text string starting at the position you specify\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B27\" value=\"LOG10\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E344\" value=\"=&apos;==&apos;\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B302\" value=\"NORMSINV\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C26\" value=\"LOG(number,base)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C303\" value=\"PEARSON(array1,array2)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B135\" value=\"COUPDAYBS\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B346\" value=\"FIXED\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C134\" value=\"AMORLINC(cost,date_purchased,first_period,salvage,period,rate,basis)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C347\" value=\"JIS(text)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D115\" value=\"Returns the exponential of a complex number\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D264\" value=\"Counts the number of blank cells within a range\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D231\" value=\"Returns the transpose of an array\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B118\" value=\"IMLOG2\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B269\" value=\"DEVSQ\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C119\" value=\"IMPOWER(inumber,number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C268\" value=\"CRITBINOM(trials,probability_s,alpha)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B226\" value=\"MATCH\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D32\" value=\"Returns a number rounded to the desired multiple\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C227\" value=\"OFFSET(reference,rows,cols,height,width)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E39\" value=\"math.radians(angle)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"F39\" value=\"import math\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B37\" value=\"PRODUCT\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C36\" value=\"POWER(number,power)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D80\" value=\"the serial number of today&apos;s date\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E362\" value=\"text.upper()\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D196\" value=\"Returns TRUE if the value is a number\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E195\" value=\"not isinstance(value, basestring)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B85\" value=\"YEARFRAC\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B360\" value=\"TEXT\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C84\" value=\"YEAR(serial_number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C361\" value=\"TRIM(text)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B193\" value=\"ISLOGICAL\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D5\" value=\"Returns the arccosine of a number\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C192\" value=\"ISEVEN(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C1\" value=\"syntax\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D181\" value=\"Returns the yield on a security that pays periodic interest\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D322\" value=\"Returns the inverse of the Student&apos;s t-distribution\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D217\" value=\"Returns the number of areas in a reference\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B176\" value=\"TBILLPRICE\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B327\" value=\"VARA\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C177\" value=\"TBILLYIELD(settlement,maturity,pr)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C326\" value=\"VAR(number1,number2,...)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B220\" value=\"COLUMNS\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D26\" value=\"Returns the logarithm of a number to a specified base\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C221\" value=\"HLOOKUP(lookup_value,table_array,row_index_num,range_lookup)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D311\" value=\"Returns the square of the Pearson product moment correlation coefficient\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D142\" value=\"Returns the cumulative principal paid on a loan between two periods\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D347\" value=\"Changes half-width (single-byte) English letters or katakana within a character string to full-width (double-byte) characters\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B31\" value=\"MOD\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E348\" value=\"text[:length]\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B306\" value=\"PERMUT\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C30\" value=\"MMULT(array1,array2)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C307\" value=\"POISSON(x,mean,cumulative)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B139\" value=\"COUPNUM\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B350\" value=\"LOWER\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C138\" value=\"COUPNCD(settlement,maturity,frequency,basis)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D127\" value=\"Converts an octal number to decimal\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D276\" value=\"Returns a frequency distribution as a vertical array\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D227\" value=\"Returns a reference offset from a given reference\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B122\" value=\"IMSIN\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B273\" value=\"FISHER\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C123\" value=\"IMSQRT(inumber)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C272\" value=\"FINV(probability,degrees_freedom1,degrees_freedom2)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B230\" value=\"RTD\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D44\" value=\"Rounds a number down, toward zero\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C231\" value=\"TRANSPOSE(array)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D40\" value=\"Returns a random number between 0 and 1\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B41\" value=\"RANDBETWEEN\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C40\" value=\"RAND( )\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D92\" value=\"Returns the modified Bessel function Kn(x)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D361\" value=\"Removes spaces from text\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B45\" value=\"ROUNDUP\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E91\" value=\"special.jn(n,x)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"F91\" value=\"from scipy import special\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C44\" value=\"ROUNDDOWN(number,num_digits)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"G90\" value=\"Note arg order is swapped\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D192\" value=\"Returns TRUE if the number is even\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"A89\" value=\"Engineering functions\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E199\" value=\"isinstance(value, basestring)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B197\" value=\"ISODD\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D1\" value=\"excel description\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E6\" value=\"math.acosh(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"F6\" value=\"import math\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B4\" value=\"ABS\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C5\" value=\"ACOS(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D177\" value=\"Returns the yield for a Treasury bill\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B180\" value=\"XNPV\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B331\" value=\"ZTEST\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C181\" value=\"YIELD(settlement,maturity,rate,pr,redemption,frequency,basis)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C330\" value=\"WEIBULL(x,alpha,beta,cumulative)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D102\" value=\"Tests whether two values are equal\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D307\" value=\"Returns the Poisson distribution\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E97\" value=\"complex(real_num, i_num)\n \nor::\n\n(X + Yj)\n::\nwhere X and Y are both integer or float literals.\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"G96\" value=\"Returns a string\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D138\" value=\"Returns the next coupon date after the settlement date\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B99\" value=\"DEC2BIN\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B310\" value=\"RANK\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C98\" value=\"CONVERT(number,from_unit,to_unit)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C311\" value=\"RSQ(known_y&apos;s,known_x&apos;s)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D150\" value=\"Returns the future value of an investment\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B143\" value=\"DB\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C142\" value=\"CUMPRINC(rate,nper,pv,start_period,end_period,type)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B147\" value=\"DOLLARFR\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C146\" value=\"DOLLARDE(fractional_dollar,fraction)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D123\" value=\"Returns the square root of a complex number\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D272\" value=\"Returns the inverse of the F probability distribution\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D239\" value=\"Returns the maximum value from selected database entries\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B126\" value=\"OCT2BIN\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B277\" value=\"FTEST\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C127\" value=\"OCT2DEC(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C276\" value=\"FREQUENCY(data_array,bins_array)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"A234\" value=\"Database\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C235\" value=\"DAVERAGE(database,field,criteria)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D52\" value=\"Returns a subtotal in a list or database\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E51\" value=\"math.sqrt( number * math.pi )\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"F51\" value=\"import math\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B49\" value=\"SINH\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E95\" value=\"hex( int( my_binary_number_as_string, 2 ) )\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C48\" value=\"SIN(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"G94\" value=\"Returns an int\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B93\" value=\"BESSELY\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C92\" value=\"BESSELK(x,n)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"G202\" value=\"Returns a Python type though, not an Excel type\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B201\" value=\"NA\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D13\" value=\"Returns the number of combinations for a given number of objects\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C200\" value=\"N(value)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E10\" value=\"math.atan2(x_num,y_num)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"F10\" value=\"import math\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D9\" value=\"Returns the arctangent of a number\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D294\" value=\"Returns the median of the given numbers\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E14\" value=\"math.cos(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"F14\" value=\"import math\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B8\" value=\"ASINH\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C9\" value=\"ATAN (number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D189\" value=\"Returns TRUE if the value is blank\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D330\" value=\"Returns the Weibull distribution\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B12\" value=\"CEILING\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B291\" value=\"LOGNORMDIST\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C13\" value=\"COMBIN(number,number_chosen)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C290\" value=\"LOGINV(probability,mean,standard_dev)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"A335\" value=\"Text functions\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D98\" value=\"Converts a number from one measurement system to another\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D319\" value=\"Calculates standard deviation based on the entire population, including numbers, text, and logical values\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E101\" value=\"oct( number ).zfill(places)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"G100\" value=\"hex(n) without the slice returns a string prepended with &apos;0x&apos;, and A-F are represented as a-f (lowercase)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B103\" value=\"ERF\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B314\" value=\"SMALL\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C102\" value=\"DELTA(number1,number2)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C315\" value=\"STANDARDIZE(x,mean,standard_dev)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D146\" value=\"Converts a dollar price, expressed as a fraction, into a dollar price, expressed as a decimal number\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B151\" value=\"FVSCHEDULE\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C150\" value=\"FV(rate,nper,pmt,pv,type)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D71\" value=\"the serial number of the last day of the month before or after a specified number of months\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D284\" value=\"Returns the hypergeometric distribution\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D235\" value=\"Returns the average of selected database entries\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B66\" value=\"DATE\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B281\" value=\"GEOMEAN\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C67\" value=\"DATEVALUE(date_text)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C280\" value=\"GAMMALN(x)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B238\" value=\"DGET\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C239\" value=\"DMAX(database,field,criteria)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D48\" value=\"Returns the sine of the given angle\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D164\" value=\"Returns the yield of a security with an odd last period\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B53\" value=\"SUM\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C52\" value=\"SUBTOTAL(function_num, ref1, ref2, ...)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D200\" value=\"Returns a value converted to a number\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E207\" value=\"logical_test and value_if_true or value_if_false\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B161\" value=\"ODDFPRICE\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C160\" value=\"NPV(rate,value1,value2, ...)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B205\" value=\"AND\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D21\" value=\"Rounds a number down, toward zero\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D290\" value=\"Returns the inverse of the lognormal distribution\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E18\" value=\"math.exp(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"F18\" value=\"import math\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B16\" value=\"DEGREES\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B295\" value=\"MIN\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C17\" value=\"EVEN(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C294\" value=\"MEDIAN(number1,number2,...)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"G336\" value=\"Only relevant in VBA\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B188\" value=\"INFO\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B339\" value=\"CLEAN\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C338\" value=\"CHAR(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D110\" value=\"Returns the imaginary coefficient of a complex number\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D315\" value=\"Returns a normalized value\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E316\" value=\"numpy.std( [number1, number2] )\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"G104\" value=\"NB. scipy version takes complex number as only parameter. not equivalent\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D263\" value=\"Counts how many values are in the list of arguments\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B107\" value=\"HEX2DEC\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C106\" value=\"HEX2BIN(number,places)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B318\" value=\"STDEVP\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D158\" value=\"Returns the annual nominal interest rate\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C319\" value=\"STDEVPA(value1,value2,...)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B258\" value=\"CHIINV\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C259\" value=\"CHITEST(actual_range,expected_range)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B155\" value=\"ISPMT\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C154\" value=\"IRR(values,guess)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D67\" value=\"a date in the form of text to a serial number\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D280\" value=\"Returns the natural logarithm of the gamma function, G(x)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E287\" value=\"reverse( sorted( my_list )[k]\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B70\" value=\"EDATE\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B285\" value=\"INTERCEPT\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C71\" value=\"EOMONTH(start_date,months)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D243\" value=\"Calculates the standard deviation based on the entire population of selected database entries\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C284\" value=\"HYPGEOMDIST(sample_s,number_sample,population_s,number_population)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B242\" value=\"DSTDEV\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C243\" value=\"DSTDEVP(database,field,criteria)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B246\" value=\"DVARP\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D60\" value=\"Returns the sum of squares of differences of corresponding values in two arrays\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D160\" value=\"Returns the net present value of an investment based on a series of periodic cash flows and a discount rate\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B57\" value=\"SUMSQ\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C56\" value=\"SUMPRODUCT(array1,array2,array3, ...)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E211\" value=\"=&apos;True&apos;\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B165\" value=\"PMT\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C164\" value=\"ODDLYIELD(settlement,maturity,last_interest,rate,pr,redemption,frequency,basis)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B209\" value=\"NOT\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D17\" value=\"Rounds a number up to the nearest even integer\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D302\" value=\"Returns the inverse of the standard normal cumulative distribution\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E22\" value=\"\n# for two numbers\nfractions.gcd( number1, number2 )\n \n# for more than two\nreduce(\n    fractions.gcd,\n    [number1, number2, number3...]\n)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"F22\" value=\"import fractions\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D133\" value=\"Returns the depreciation for each accounting period by using a depreciation coefficient\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D338\" value=\"Returns the character specified by the code number\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B20\" value=\"FACTDOUBLE\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E341\" value=\"text1 + text2\n::\nor::\n\nseparator = &apos;X&apos;\nseparator.join(text1, text2...)\n\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B299\" value=\"NORMDIST\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C21\" value=\"FLOOR(number,significance)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C298\" value=\"NEGBINOMDIST(number_f,number_s,probability_s)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B128\" value=\"OCT2HEX\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B343\" value=\"DOLLAR\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D106\" value=\"Converts a hexadecimal number to binary\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E109\" value=\"abs(inumber)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D118\" value=\"Returns the base-2 logarithm of a complex number\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D259\" value=\"Returns the test for independence\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B111\" value=\"IMARGUMENT\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C110\" value=\"IMAGINARY(inumber)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"G261\" value=\"See numpy.corrcoef\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D154\" value=\"Returns the internal rate of return for a series of cash flows\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B115\" value=\"IMEXP\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B262\" value=\"COUNT\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C114\" value=\"IMDIV(inumber1,inumber2)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C263\" value=\"COUNTA(value1,value2,...)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B159\" value=\"NPER\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C158\" value=\"NOMINAL(effect_rate,npery)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D79\" value=\"a time in the form of text to a serial number\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D356\" value=\"Returns the rightmost characters from a text value\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E355\" value=\"text * number_times\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B74\" value=\"MONTH\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B353\" value=\"PROPER\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C75\" value=\"NETWORKDAYS(start_date,end_date,holidays)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D255\" value=\"Returns the inverse of the cumulative distribution function for a specified beta distribution\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C352\" value=\"PHONETIC(reference)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B250\" value=\"AVERAGE\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D56\" value=\"Returns the sum of the products of corresponding array components\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C251\" value=\"AVERAGEA(value1,value2,...)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E63\" value=\"math.trunc(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"F63\" value=\"import math\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D172\" value=\"Returns the amount received at maturity for a fully invested security\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B61\" value=\"TAN\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C60\" value=\"SUMXMY2(array_x,array_y)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D208\" value=\"Returns a value you specify if a formula evaluates to an error; otherwise, returns the result of the formula\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B169\" value=\"PRICEMAT\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C168\" value=\"PRICEDISC(settlement,maturity,discount,redemption,basis)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D29\" value=\"Returns the matrix inverse of an array\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D298\" value=\"Returns the negative binomial distribution\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E26\" value=\"math.log(number, base)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"F26\" value=\"import math\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B24\" value=\"LCM\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D350\" value=\"Converts text to lowercase\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E345\" value=\"text.find( &apos;text_to_find&apos; )\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B303\" value=\"PEARSON\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C25\" value=\"LN(number)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C302\" value=\"NORMSINV(probability)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B132\" value=\"ACCRINTM\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B347\" value=\"JIS\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C133\" value=\"AMORDEGRC(cost,date_purchased,first_period,salvage,period,rate,basis)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C346\" value=\"FIXED(number,decimals,no_commas)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D114\" value=\"Returns the quotient of two complex numbers\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D271\" value=\"Returns the F probability distribution\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D230\" value=\"Retrieves real-time data from a program that supports COM automation (Automation: A way to work with an application&apos;s objects from another application or development tool. Formerly called OLE Automation, Automation is an industry standard and a feature of the Component Object Model (COM).)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B119\" value=\"IMPOWER\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B266\" value=\"COUNTIFS\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C118\" value=\"IMLOG2(inumber)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C267\" value=\"COVAR(array1,array2)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B227\" value=\"OFFSET\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D39\" value=\"Converts degrees to radians\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C226\" value=\"MATCH(lookup_value,lookup_array,match_type)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D75\" value=\"the number of whole workdays between two dates\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D352\" value=\"Extracts the phonetic (furigana) characters from a text string\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B34\" value=\"ODD\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E76\" value=\"datetime.datetime.now()\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E359\" value=\"str(value)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C35\" value=\"PI( )\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E363\" value=\"int(text)\n::\nor::\n\nfloat(text)\n\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B78\" value=\"TIME\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B357\" value=\"SEARCH, SEARCHB\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C79\" value=\"TIMEVALUE(time_text)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D251\" value=\"Returns the average of its arguments, including numbers, text, and logical values\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B361\" value=\"TRIM\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C360\" value=\"TEXT(value,format_text)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B254\" value=\"BETADIST\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D4\" value=\"Returns the absolute value of a number\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C255\" value=\"BETAINV(probability,alpha,beta,A,B)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D168\" value=\"Returns the price per $100 face value of a discounted security\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"A1\" value=\"category\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D325\" value=\"Returns the probability associated with a Student&apos;s t-test\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B1\" value=\"function\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D220\" value=\"Returns the number of columns in a reference\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B173\" value=\"SLN\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B320\" value=\"STEYX\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C172\" value=\"RECEIVED(settlement,maturity,investment,discount,basis)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C321\" value=\"TDIST(x,degrees_freedom,tails)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D216\" value=\"Returns a reference as text to a single cell in a worksheet\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B217\" value=\"AREAS\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C216\" value=\"ADDRESS(row_num,column_num,abs_num,a1,sheet_text)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B221\" value=\"HLOOKUP\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D25\" value=\"Returns the natural logarithm of a number\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C220\" value=\"COLUMNS(array)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D310\" value=\"Returns the rank of a number in a list of numbers\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D141\" value=\"Returns the cumulative interest paid between two periods\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D346\" value=\"Formats a number as text with a fixed number of decimals\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B28\" value=\"MDETERM\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E349\" value=\"len(text)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B307\" value=\"POISSON\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C29\" value=\"MINVERSE(array)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C306\" value=\"PERMUT(number,number_chosen)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B136\" value=\"COUPDAYS\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B351\" value=\"MID, MIDB\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C137\" value=\"COUPDAYSNC(settlement,maturity,frequency,basis)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C350\" value=\"LOWER(text)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D126\" value=\"Converts an octal number to binary\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D267\" value=\"Returns covariance, the average of the products of paired deviations\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D226\" value=\"Looks up values in a reference or array\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B123\" value=\"IMSQRT\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B270\" value=\"EXPONDIST\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C122\" value=\"IMSIN(inumber)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C271\" value=\"FDIST(x,degrees_freedom1,degrees_freedom2)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B231\" value=\"TRANSPOSE\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D35\" value=\"Returns the value of pi\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C230\" value=\"RTD(ProgID,server,topic1,[topic2],...)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E36\" value=\"a**b\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B38\" value=\"QUOTIENT\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E80\" value=\"datetime.date.today()\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C39\" value=\"RADIANS(angle)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D83\" value=\"the serial number of the date before or after a specified number of workdays\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"G81\" value=\"Alternatively, consider date.weekday(), which is 0-indexed on Monday by default. Both options require a date object, instead of taking a serial number.\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D360\" value=\"Formats a number and converts it to text\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"B82\" value=\"WEEKNUM\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"C83\" value=\"WORKDAY(start_date,days,holidays)\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"D199\" value=\"Returns TRUE if the value is text\" type=\"general\">\r\n        </cell>\r\n        <cell name=\"E192\" value=\"number % 2 == 0\" type=\"general\">\r\n        </cell>\r\n    </worksheet>\r\n    <postFormulaeUserCode><![CDATA[\r\nTITLE = 'Common Spreadsheet Functions in Dirigible'\r\nUNFINISHED_MARKER = 'Coming soon'\r\nUNFINISHED_LINK = '`' + UNFINISHED_MARKER + '`_'\r\n\r\nSUPPORT_TEXT = '''\r\n.. _''' + UNFINISHED_MARKER + ''':\r\n'''\r\n\r\ndef get_definitions():\r\n    definitions = []\r\n    for cell in mt.Cols['function'].Cells:\r\n        if cell.Value:\r\n            new = FunctionDef()\r\n            new._row = cell.Row\r\n            new.excel_function_name = cell.Value or ''\r\n            new.excel_syntax = mt.Cols['syntax'][cell.Row] or mt.Cols['function'][cell.Row] + '()'\r\n            new.excel_description = mt.Cols['excel description'][cell.Row] or ''\r\n            new.howto_in_resolver = mt.Cols['howto in resolver'][cell.Row]\r\n            new.imports = mt.Cols['imports'][cell.Row]\r\n            new.comments = mt.Cols['comments'][cell.Row]\r\n            definitions.append(new)\r\n    return definitions\r\n\r\ndef output_title(title, underline, out_file):\r\n    print >> out_file,  title\r\n    print >> out_file,  underline * len(title)\r\n    print >> out_file,  ''\r\n    \r\ndef output_header(out_file):\r\n    output_title(TITLE, '=', out_file)\r\n    print >> out_file, SUPPORT_TEXT\r\n\r\ndef format_syntax(syntax):\r\n    syntax = syntax.replace(' (', '(')\r\n    syntax = syntax.replace('(', '( ')\r\n    syntax = syntax.replace(')', ' )')\r\n    syntax = syntax.replace(',', ', ')\r\n    syntax = '| **' + syntax.replace('(', '**\\ (')    \r\n    return syntax\r\n\r\ndef get_func_desc(syntax, description):\r\n    lines = [\r\n        format_syntax(syntax),\r\n        '',\r\n        r'\\ *%s*' % (description,),\r\n    ]\r\n    return lines\r\n\r\ndef get_python_equiv(imports, howto, comment):\r\n    lines = []\r\n\r\n    if imports or howto:\r\n        lines.extend(['::', ''])\r\n        if imports:\r\n            lines.append(' ' + imports)\r\n        if howto:\r\n            indent = ' '\r\n            for h in howto.split('\\n'):\r\n                if h == '::':\r\n                    indent = ''\r\n                    lines.append('')\r\n                    continue\r\n                elif h.endswith('::'):\r\n                    lines.append(h)\r\n                else:\r\n                    lines.append(indent + h)\r\n                indent = ' '\r\n        lines.append('')\r\n\r\n    if comment:\r\n        lines.append(r'\\ *%s*' % (comment,))\r\n\r\n    if not (howto or comment):\r\n        lines.append(UNFINISHED_LINK)\r\n\r\n    return lines\r\n\r\ndef get_row(defn):\r\n    return [\r\n        get_func_desc(defn.excel_syntax, defn.excel_description),\r\n        get_python_equiv(defn.imports, defn.howto_in_resolver, defn.comments),\r\n    ]\r\n\r\ndef get_col_widths(headings, rows):\r\n    col_widths = map(len, headings)\r\n    for row in rows:\r\n        for index, col in enumerate(row):\r\n            for line in col:\r\n                col_widths[index] = max(col_widths[index], len(line))\r\n    return col_widths\r\n\r\ndef get_row_divider(col_widths):\r\n    table_line = '+'\r\n    for width in col_widths:\r\n        table_line += '-' * width + '+'\r\n    return table_line\r\n\r\ndef output_row(row, col_widths, out_file):\r\n    for line_parts in izip_longest(*row):\r\n        line = '|'\r\n        for index, line_part in enumerate(line_parts):\r\n            text = line_part if line_part else ''\r\n            spacing = ' ' * (col_widths[index] - len(text))\r\n            line += text + spacing + '|'\r\n        print >> out_file, line\r\n    return line\r\n\r\n\r\ndef output_category(cat, definitions, out_file):\r\n    output_title(cat, '-', out_file)\r\n    out_headings = ['Function', 'Python Equivalent']\r\n    rows = [get_row(defn) for defn in definitions]\r\n    col_widths = get_col_widths(out_headings, rows)\r\n    row_divider = get_row_divider(col_widths)\r\n\r\n    print >> out_file, row_divider\r\n    output_row([[h] for h in out_headings], col_widths, out_file)\r\n    print >> out_file, row_divider.replace('-', '=')\r\n    for row in rows:\r\n        output_row(row, col_widths, out_file)\r\n        print >> out_file, row_divider\r\n    print >> out_file,  ''\r\n\r\n\r\ndef output_rst():\r\n    definitions = get_definitions()\r\n    out_file = file(join(dirname(__file__), 'spreadsheet-functions.rst'), 'w')\r\n    try:\r\n        output_header(out_file)\r\n        categories = [c.Value for c in mt.Cols['category'].Cells if c.Value]\r\n        for category in categories[1:]:\r\n            output_category(\r\n                category,\r\n                [d for d in definitions if d.category==category],\r\n                out_file)\r\n    finally:    \r\n        out_file.close()\r\n\r\noutput_rst()\r\n]]></postFormulaeUserCode>\r\n</document>\r\n"
  },
  {
    "path": "documentation/spreadsheet-functions.rst",
    "content": "Common Spreadsheet Functions in Dirigible\r\n=========================================\r\n\r\n\r\n\r\nMath and trigonometry functions\r\n-------------------------------\r\n\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|Function                                                                              |Python Equivalent                                                            |\r\n+======================================================================================+=============================================================================+\r\n|| **ABS**\\ ( number )                                                                 |::                                                                           |\r\n|                                                                                      |                                                                             |\r\n|\\ *Returns the absolute value of a number*                                            | abs(number)                                                                 |\r\n|                                                                                      |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **ACOS**\\ ( number )                                                                |::                                                                           |\r\n|                                                                                      |                                                                             |\r\n|\\ *Returns the arccosine of a number*                                                 | import math                                                                 |\r\n|                                                                                      | math.acos(number)                                                           |\r\n|                                                                                      |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **ACOSH**\\ ( number )                                                               |::                                                                           |\r\n|                                                                                      |                                                                             |\r\n|\\ *Returns the inverse hyperbolic cosine of a number*                                 | import math                                                                 |\r\n|                                                                                      | math.acosh(number)                                                          |\r\n|                                                                                      |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **ASIN**\\ ( number )                                                                |::                                                                           |\r\n|                                                                                      |                                                                             |\r\n|\\ *Returns the arcsine of a number*                                                   | import math                                                                 |\r\n|                                                                                      | math.asin(number)                                                           |\r\n|                                                                                      |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **ASINH**\\ ( number )                                                               |::                                                                           |\r\n|                                                                                      |                                                                             |\r\n|\\ *Returns the inverse hyperbolic sine of a number*                                   | import math                                                                 |\r\n|                                                                                      | math.asinh(number)                                                          |\r\n|                                                                                      |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **ATAN**\\ ( number )                                                                |::                                                                           |\r\n|                                                                                      |                                                                             |\r\n|\\ *Returns the arctangent of a number*                                                | import math                                                                 |\r\n|                                                                                      | math.atan(number)                                                           |\r\n|                                                                                      |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **ATAN2**\\ ( x_num, y_num )                                                         |::                                                                           |\r\n|                                                                                      |                                                                             |\r\n|\\ *Returns the arctangent from x- and y-coordinates*                                  | import math                                                                 |\r\n|                                                                                      | math.atan2(x_num,y_num)                                                     |\r\n|                                                                                      |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **ATANH**\\ ( number )                                                               |::                                                                           |\r\n|                                                                                      |                                                                             |\r\n|\\ *Returns the inverse hyperbolic tangent of a number*                                | import math                                                                 |\r\n|                                                                                      | math.atanh(number)                                                          |\r\n|                                                                                      |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **CEILING**\\ ( number, significance )                                               |::                                                                           |\r\n|                                                                                      |                                                                             |\r\n|\\ *Rounds a number to the nearest integer or to the nearest multiple of significance* | import math                                                                 |\r\n|                                                                                      | math.ceil(number)                                                           |\r\n|                                                                                      |                                                                             |\r\n|                                                                                      |\\ *NB. does not handle the second 'significance' argument*                   |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **COMBIN**\\ ( number, number_chosen )                                               |::                                                                           |\r\n|                                                                                      |                                                                             |\r\n|\\ *Returns the number of combinations for a given number of objects*                  | import itertools                                                            |\r\n|                                                                                      | len( itertools.product( range(number), repeat=number_chosen ) )             |\r\n|                                                                                      |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **COS**\\ ( number )                                                                 |::                                                                           |\r\n|                                                                                      |                                                                             |\r\n|\\ *Returns the cosine of a number*                                                    | import math                                                                 |\r\n|                                                                                      | math.cos(number)                                                            |\r\n|                                                                                      |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **COSH**\\ ( number )                                                                |::                                                                           |\r\n|                                                                                      |                                                                             |\r\n|\\ *Returns the hyperbolic cosine of a number*                                         | import math                                                                 |\r\n|                                                                                      | math.cosh(number)                                                           |\r\n|                                                                                      |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **DEGREES**\\ ( angle )                                                              |::                                                                           |\r\n|                                                                                      |                                                                             |\r\n|\\ *Converts radians to degrees*                                                       | import math                                                                 |\r\n|                                                                                      | math.degrees(angle)                                                         |\r\n|                                                                                      |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **EVEN**\\ ( number )                                                                |::                                                                           |\r\n|                                                                                      |                                                                             |\r\n|\\ *Rounds a number up to the nearest even integer*                                    | import math                                                                 |\r\n|                                                                                      | int(math.ceil(number / 2.0)) * 2                                            |\r\n|                                                                                      |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **EXP**\\ (  )                                                                       |::                                                                           |\r\n|                                                                                      |                                                                             |\r\n|\\ *Returns e raised to the power of a given number*                                   | import math                                                                 |\r\n|                                                                                      | math.exp(number)                                                            |\r\n|                                                                                      |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **FACT**\\ ( number )                                                                |::                                                                           |\r\n|                                                                                      |                                                                             |\r\n|\\ *Returns the factorial of a number*                                                 | import math                                                                 |\r\n|                                                                                      | math.factorial(number)                                                      |\r\n|                                                                                      |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **FACTDOUBLE**\\ ( number )                                                          |::                                                                           |\r\n|                                                                                      |                                                                             |\r\n|\\ *Returns the double factorial of a number*                                          | import scipy                                                                |\r\n|                                                                                      | scipy.misc.factorial2( number )                                             |\r\n|                                                                                      |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **FLOOR**\\ ( number, significance )                                                 |::                                                                           |\r\n|                                                                                      |                                                                             |\r\n|\\ *Rounds a number down, toward zero*                                                 | import math                                                                 |\r\n|                                                                                      | math.floor(number)                                                          |\r\n|                                                                                      |                                                                             |\r\n|                                                                                      |\\ *NB. does not handle the second 'significance' argument*                   |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **GCD**\\ ( number1, number2,  ... )                                                 |::                                                                           |\r\n|                                                                                      |                                                                             |\r\n|\\ *Returns the greatest common divisor*                                               | import fractions                                                            |\r\n|                                                                                      |                                                                             |\r\n|                                                                                      | # for two numbers                                                           |\r\n|                                                                                      | fractions.gcd( number1, number2 )                                           |\r\n|                                                                                      |                                                                             |\r\n|                                                                                      | # for more than two                                                         |\r\n|                                                                                      | reduce(                                                                     |\r\n|                                                                                      |     fractions.gcd,                                                          |\r\n|                                                                                      |     [number1, number2, number3...]                                          |\r\n|                                                                                      | )                                                                           |\r\n|                                                                                      |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **INT**\\ ( number )                                                                 |::                                                                           |\r\n|                                                                                      |                                                                             |\r\n|\\ *Rounds a number down to the nearest integer*                                       | int(number)                                                                 |\r\n|                                                                                      |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **LCM**\\ ( number1, number2,  ... )                                                 |::                                                                           |\r\n|                                                                                      |                                                                             |\r\n|\\ *Returns the least common multiple*                                                 | def lcm(a, b):                                                              |\r\n|                                                                                      |     return a * b // gcd(a, b)                                               |\r\n|                                                                                      |                                                                             |\r\n|                                                                                      | def lcmm(*args):                                                            |\r\n|                                                                                      |     return reduce(lcm, args)                                                |\r\n|                                                                                      |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **LN**\\ ( number )                                                                  |::                                                                           |\r\n|                                                                                      |                                                                             |\r\n|\\ *Returns the natural logarithm of a number*                                         | import math                                                                 |\r\n|                                                                                      | math.log(number)                                                            |\r\n|                                                                                      |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **LOG**\\ ( number, base )                                                           |::                                                                           |\r\n|                                                                                      |                                                                             |\r\n|\\ *Returns the logarithm of a number to a specified base*                             | import math                                                                 |\r\n|                                                                                      | math.log(number, base)                                                      |\r\n|                                                                                      |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **LOG10**\\ ( number )                                                               |::                                                                           |\r\n|                                                                                      |                                                                             |\r\n|\\ *Returns the base-10 logarithm of a number*                                         | import math                                                                 |\r\n|                                                                                      | math.log10(number)                                                          |\r\n|                                                                                      |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **MDETERM**\\ ( array )                                                              |`Coming soon`_                                                               |\r\n|                                                                                      |                                                                             |\r\n|\\ *Returns the matrix determinant of an array*                                        |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **MINVERSE**\\ ( array )                                                             |`Coming soon`_                                                               |\r\n|                                                                                      |                                                                             |\r\n|\\ *Returns the matrix inverse of an array*                                            |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **MMULT**\\ (  )                                                                     |`Coming soon`_                                                               |\r\n|                                                                                      |                                                                             |\r\n|\\ *Returns the matrix product of two arrays*                                          |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **MOD**\\ (  )                                                                       |::                                                                           |\r\n|                                                                                      |                                                                             |\r\n|\\ *Returns the remainder from division*                                               | number % divisor                                                            |\r\n|                                                                                      |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **MROUND**\\ ( number, multiple )                                                    |`Coming soon`_                                                               |\r\n|                                                                                      |                                                                             |\r\n|\\ *Returns a number rounded to the desired multiple*                                  |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **MULTINOMIAL**\\ ( number1, number2,  ... )                                         |`Coming soon`_                                                               |\r\n|                                                                                      |                                                                             |\r\n|\\ *Returns the multinomial of a set of numbers*                                       |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **ODD**\\ (  )                                                                       |`Coming soon`_                                                               |\r\n|                                                                                      |                                                                             |\r\n|\\ *Rounds a number up to the nearest odd integer*                                     |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **PI**\\ (   )                                                                       |::                                                                           |\r\n|                                                                                      |                                                                             |\r\n|\\ *Returns the value of pi*                                                           | import math                                                                 |\r\n|                                                                                      | math.pi                                                                     |\r\n|                                                                                      |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **POWER**\\ (  )                                                                     |::                                                                           |\r\n|                                                                                      |                                                                             |\r\n|\\ *Returns the result of a number raised to a power*                                  | a**b                                                                        |\r\n|                                                                                      |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **PRODUCT**\\ ( number1, number2, ... )                                              |::                                                                           |\r\n|                                                                                      |                                                                             |\r\n|\\ *Multiplies its arguments*                                                          | # for two numbers                                                           |\r\n|                                                                                      | number1 * number2                                                           |\r\n|                                                                                      |                                                                             |\r\n|                                                                                      | # for more than two                                                         |\r\n|                                                                                      | import operator                                                             |\r\n|                                                                                      | reduce(operator.mul, [number1, number2, number3...])                        |\r\n|                                                                                      |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **QUOTIENT**\\ (  )                                                                  |::                                                                           |\r\n|                                                                                      |                                                                             |\r\n|\\ *Returns the integer portion of a division*                                         | import math                                                                 |\r\n|                                                                                      | math.floor( numerator / denominator )                                       |\r\n|                                                                                      |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **RADIANS**\\ ( angle )                                                              |::                                                                           |\r\n|                                                                                      |                                                                             |\r\n|\\ *Converts degrees to radians*                                                       | import math                                                                 |\r\n|                                                                                      | math.radians(angle)                                                         |\r\n|                                                                                      |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **RAND**\\ (   )                                                                     |::                                                                           |\r\n|                                                                                      |                                                                             |\r\n|\\ *Returns a random number between 0 and 1*                                           | import random                                                               |\r\n|                                                                                      | random.random()                                                             |\r\n|                                                                                      |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **RANDBETWEEN**\\ ( bottom, top )                                                    |::                                                                           |\r\n|                                                                                      |                                                                             |\r\n|\\ *Returns a random number between the numbers you specify*                           | import random                                                               |\r\n|                                                                                      | random.randint( bottom, top ) or random.randrange( bottom, top )            |\r\n|                                                                                      |                                                                             |\r\n|                                                                                      |\\ *Note minor difference between randint and randrange, <bottom and <=bottom*|\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **ROMAN**\\ ( number, form )                                                         |`Coming soon`_                                                               |\r\n|                                                                                      |                                                                             |\r\n|\\ *Converts an arabic numeral to roman, as text*                                      |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **ROUND**\\ ( number, num_digits )                                                   |`Coming soon`_                                                               |\r\n|                                                                                      |                                                                             |\r\n|\\ *Rounds a number to a specified number of digits*                                   |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **ROUNDDOWN**\\ ( number, num_digits )                                               |`Coming soon`_                                                               |\r\n|                                                                                      |                                                                             |\r\n|\\ *Rounds a number down, toward zero*                                                 |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **ROUNDUP**\\ ( number, num_digits )                                                 |`Coming soon`_                                                               |\r\n|                                                                                      |                                                                             |\r\n|\\ *Rounds a number up, away from zero*                                                |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **SERIESSUM**\\ ( x, n, m, coefficients )                                            |::                                                                           |\r\n|                                                                                      |                                                                             |\r\n|\\ *Returns the sum of a power series based on the formula*                            | sum(                                                                        |\r\n|                                                                                      |     c * x ** pwr                                                            |\r\n|                                                                                      |     for c, pwd in zip(                                                      |\r\n|                                                                                      |         coefficients,                                                       |\r\n|                                                                                      |         range(n, len( coefficients ), m)                                    |\r\n|                                                                                      |     )                                                                       |\r\n|                                                                                      | )                                                                           |\r\n|                                                                                      |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **SIGN**\\ ( number )                                                                |`Coming soon`_                                                               |\r\n|                                                                                      |                                                                             |\r\n|\\ *Returns the sign of a number*                                                      |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **SIN**\\ ( number )                                                                 |::                                                                           |\r\n|                                                                                      |                                                                             |\r\n|\\ *Returns the sine of the given angle*                                               | import math                                                                 |\r\n|                                                                                      | math.sin(number)                                                            |\r\n|                                                                                      |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **SINH**\\ ( number )                                                                |::                                                                           |\r\n|                                                                                      |                                                                             |\r\n|\\ *Returns the hyperbolic sine of a number*                                           | import math                                                                 |\r\n|                                                                                      | math.sinh(number)                                                           |\r\n|                                                                                      |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **SQRT**\\ ( number )                                                                |::                                                                           |\r\n|                                                                                      |                                                                             |\r\n|\\ *Returns a positive square root*                                                    | import math                                                                 |\r\n|                                                                                      | math.sqrt(number)                                                           |\r\n|                                                                                      |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **SQRTPI**\\ ( number )                                                              |::                                                                           |\r\n|                                                                                      |                                                                             |\r\n|\\ *Returns the square root of (number * pi)*                                          | import math                                                                 |\r\n|                                                                                      | math.sqrt( number * math.pi )                                               |\r\n|                                                                                      |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **SUBTOTAL**\\ ( function_num,  ref1,  ref2,  ... )                                  |`Coming soon`_                                                               |\r\n|                                                                                      |                                                                             |\r\n|\\ *Returns a subtotal in a list or database*                                          |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **SUM**\\ ( number1, number2,  ... )                                                 |::                                                                           |\r\n|                                                                                      |                                                                             |\r\n|\\ *Adds its arguments*                                                                | sum(list_of_numbers)                                                        |\r\n|                                                                                      |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **SUMIF**\\ ( range, criteria, sum_range )                                           |`Coming soon`_                                                               |\r\n|                                                                                      |                                                                             |\r\n|\\ *Adds the cells specified by a given criteria*                                      |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **SUMIFS**\\ (  )                                                                    |`Coming soon`_                                                               |\r\n|                                                                                      |                                                                             |\r\n|\\ *Adds the cells in a range that meet multiple criteria*                             |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **SUMPRODUCT**\\ ( array1, array2, array3,  ... )                                    |`Coming soon`_                                                               |\r\n|                                                                                      |                                                                             |\r\n|\\ *Returns the sum of the products of corresponding array components*                 |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **SUMSQ**\\ ( number1, number2,  ... )                                               |`Coming soon`_                                                               |\r\n|                                                                                      |                                                                             |\r\n|\\ *Returns the sum of the squares of the arguments*                                   |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **SUMX2MY2**\\ ( array_x, array_y )                                                  |`Coming soon`_                                                               |\r\n|                                                                                      |                                                                             |\r\n|\\ *Returns the sum of the difference of squares of corresponding values in two arrays*|                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **SUMX2PY2**\\ ( array_x, array_y )                                                  |`Coming soon`_                                                               |\r\n|                                                                                      |                                                                             |\r\n|\\ *Returns the sum of the sum of squares of corresponding values in two arrays*       |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **SUMXMY2**\\ ( array_x, array_y )                                                   |`Coming soon`_                                                               |\r\n|                                                                                      |                                                                             |\r\n|\\ *Returns the sum of squares of differences of corresponding values in two arrays*   |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **TAN**\\ ( number )                                                                 |::                                                                           |\r\n|                                                                                      |                                                                             |\r\n|\\ *Returns the tangent of a number*                                                   | import math                                                                 |\r\n|                                                                                      | math.tan(number)                                                            |\r\n|                                                                                      |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **TANH**\\ ( number )                                                                |::                                                                           |\r\n|                                                                                      |                                                                             |\r\n|\\ *Returns the hyperbolic tangent of a number*                                        | import math                                                                 |\r\n|                                                                                      | math.tanh(number)                                                           |\r\n|                                                                                      |                                                                             |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n|| **TRUNC**\\ ( number, num_digits )                                                   |::                                                                           |\r\n|                                                                                      |                                                                             |\r\n|\\ *Truncates a number to an integer*                                                  | import math                                                                 |\r\n|                                                                                      | math.trunc(number)                                                          |\r\n|                                                                                      |                                                                             |\r\n|                                                                                      |\\ *NB. Does not take second parameter.*                                      |\r\n+--------------------------------------------------------------------------------------+-----------------------------------------------------------------------------+\r\n\r\nDate and time\r\n-------------\r\n\r\n+-------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n|Function                                                                                               |Python Equivalent                                                                                                                                          |\r\n+=======================================================================================================+===========================================================================================================================================================+\r\n|| **DATE**\\ ( year, month, day )                                                                       |::                                                                                                                                                         |\r\n|                                                                                                       |                                                                                                                                                           |\r\n|\\ *the serial number of a particular date*                                                             | import datetime                                                                                                                                           |\r\n|                                                                                                       | datetime.datetime( yyyy, mm, dd)                                                                                                                          |\r\n|                                                                                                       |                                                                                                                                                           |\r\n|                                                                                                       |or::                                                                                                                                                       |\r\n|                                                                                                       |                                                                                                                                                           |\r\n|                                                                                                       | datetime.date( yyyy, mm, dd )                                                                                                                             |\r\n|                                                                                                       |                                                                                                                                                           |\r\n|                                                                                                       |\\ *NB. Neither datetime.datetime nor datetime.date add to integers to give a new date.*                                                                    |\r\n+-------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n|| **DATEVALUE**\\ ( date_text )                                                                         |::                                                                                                                                                         |\r\n|                                                                                                       |                                                                                                                                                           |\r\n|\\ *a date in the form of text to a serial number*                                                      | import datetime                                                                                                                                           |\r\n|                                                                                                       | format = \"%d/%m/%Y\" # for example                                                                                                                         |\r\n|                                                                                                       | datetime.datetime.strptime( date_text, format )                                                                                                           |\r\n|                                                                                                       |                                                                                                                                                           |\r\n+-------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n|| **DAY**\\ ( serial_number )                                                                           |`Coming soon`_                                                                                                                                             |\r\n|                                                                                                       |                                                                                                                                                           |\r\n|\\ *a serial number to a day of the month*                                                              |                                                                                                                                                           |\r\n+-------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n|| **DAYS360**\\ ( start_date, end_date, method )                                                        |`Coming soon`_                                                                                                                                             |\r\n|                                                                                                       |                                                                                                                                                           |\r\n|\\ *the number of days between two dates based on a 360-day year*                                       |                                                                                                                                                           |\r\n+-------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n|| **EDATE**\\ ( start_date, months )                                                                    |`Coming soon`_                                                                                                                                             |\r\n|                                                                                                       |                                                                                                                                                           |\r\n|\\ *the serial number of the date that is the indicated number of months before or after the start date*|                                                                                                                                                           |\r\n+-------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n|| **EOMONTH**\\ ( start_date, months )                                                                  |`Coming soon`_                                                                                                                                             |\r\n|                                                                                                       |                                                                                                                                                           |\r\n|\\ *the serial number of the last day of the month before or after a specified number of months*        |                                                                                                                                                           |\r\n+-------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n|| **HOUR**\\ ( serial_number )                                                                          |`Coming soon`_                                                                                                                                             |\r\n|                                                                                                       |                                                                                                                                                           |\r\n|\\ *a serial number to an hour*                                                                         |                                                                                                                                                           |\r\n+-------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n|| **MINUTE**\\ ( serial_number )                                                                        |`Coming soon`_                                                                                                                                             |\r\n|                                                                                                       |                                                                                                                                                           |\r\n|\\ *a serial number to a minute*                                                                        |                                                                                                                                                           |\r\n+-------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n|| **MONTH**\\ ( serial_number )                                                                         |`Coming soon`_                                                                                                                                             |\r\n|                                                                                                       |                                                                                                                                                           |\r\n|\\ *a serial number to a month*                                                                         |                                                                                                                                                           |\r\n+-------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n|| **NETWORKDAYS**\\ ( start_date, end_date, holidays )                                                  |\\ *Implemented by this third-party package:http://pypi.python.org/pypi/workdays/*                                                                          |\r\n|                                                                                                       |                                                                                                                                                           |\r\n|\\ *the number of whole workdays between two dates*                                                     |                                                                                                                                                           |\r\n+-------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n|| **NOW**\\ (   )                                                                                       |::                                                                                                                                                         |\r\n|                                                                                                       |                                                                                                                                                           |\r\n|\\ *the serial number of the current date and time*                                                     | datetime.datetime.now()                                                                                                                                   |\r\n|                                                                                                       |                                                                                                                                                           |\r\n|                                                                                                       |\\ *Instead of a serial number, returns a datetime object, with properties such as '.hour'.*                                                                |\r\n+-------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n|| **SECOND**\\ ( serial_number )                                                                        |`Coming soon`_                                                                                                                                             |\r\n|                                                                                                       |                                                                                                                                                           |\r\n|\\ *a serial number to a second*                                                                        |                                                                                                                                                           |\r\n+-------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n|| **TIME**\\ ( hour, minute, second )                                                                   |`Coming soon`_                                                                                                                                             |\r\n|                                                                                                       |                                                                                                                                                           |\r\n|\\ *the serial number of a particular time*                                                             |                                                                                                                                                           |\r\n+-------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n|| **TIMEVALUE**\\ ( time_text )                                                                         |::                                                                                                                                                         |\r\n|                                                                                                       |                                                                                                                                                           |\r\n|\\ *a time in the form of text to a serial number*                                                      | datetime.strptime( time_text, python_date_string_format )                                                                                                 |\r\n|                                                                                                       |                                                                                                                                                           |\r\n|                                                                                                       |\\ *Returns a datetime object*                                                                                                                              |\r\n+-------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n|| **TODAY**\\ (   )                                                                                     |::                                                                                                                                                         |\r\n|                                                                                                       |                                                                                                                                                           |\r\n|\\ *the serial number of today's date*                                                                  | datetime.date.today()                                                                                                                                     |\r\n|                                                                                                       |                                                                                                                                                           |\r\n|                                                                                                       |\\ *Instead of a serial number, returns a date object, with properties such as '.year'.*                                                                    |\r\n+-------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n|| **WEEKDAY**\\ ( serial_number, return_type )                                                          |::                                                                                                                                                         |\r\n|                                                                                                       |                                                                                                                                                           |\r\n|\\ *a serial number to a day of the week*                                                               | date.isoweekday()                                                                                                                                         |\r\n|                                                                                                       |                                                                                                                                                           |\r\n|                                                                                                       |\\ *Alternatively, consider date.weekday(), which is 0-indexed on Monday by default. Both options require a date object, instead of taking a serial number.*|\r\n+-------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n|| **WEEKNUM**\\ ( serial_num, return_type )                                                             |::                                                                                                                                                         |\r\n|                                                                                                       |                                                                                                                                                           |\r\n|\\ *a serial number to a number representing where the week falls numerically with a year*              | date.isocalendar()[1]                                                                                                                                     |\r\n|                                                                                                       |                                                                                                                                                           |\r\n+-------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n|| **WORKDAY**\\ ( start_date, days, holidays )                                                          |\\ *Implemented by this third-party package: http://pypi.python.org/pypi/workdays/*                                                                         |\r\n|                                                                                                       |                                                                                                                                                           |\r\n|\\ *the serial number of the date before or after a specified number of workdays*                       |                                                                                                                                                           |\r\n+-------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n|| **YEAR**\\ ( serial_number )                                                                          |`Coming soon`_                                                                                                                                             |\r\n|                                                                                                       |                                                                                                                                                           |\r\n|\\ *a serial number to a year*                                                                          |                                                                                                                                                           |\r\n+-------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n|| **YEARFRAC**\\ ( start_date, end_date, basis )                                                        |`Coming soon`_                                                                                                                                             |\r\n|                                                                                                       |                                                                                                                                                           |\r\n|\\ *the year fraction representing the number of whole days between start_date and end_date*            |                                                                                                                                                           |\r\n+-------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------+\r\n\r\nEngineering functions\r\n---------------------\r\n\r\n+------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------+\r\n|Function                                                          |Python Equivalent                                                                                            |\r\n+==================================================================+=============================================================================================================+\r\n|| **BESSELI**\\ ( x, n )                                           |::                                                                                                           |\r\n|                                                                  |                                                                                                             |\r\n|\\ *Returns the modified Bessel function In(x)*                    | from scipy import special                                                                                   |\r\n|                                                                  | special.iv(n,x)                                                                                             |\r\n|                                                                  |                                                                                                             |\r\n|                                                                  |\\ *Note arg order is swapped*                                                                                |\r\n+------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------+\r\n|| **BESSELJ**\\ ( x, n )                                           |::                                                                                                           |\r\n|                                                                  |                                                                                                             |\r\n|\\ *Returns the Bessel function Jn(x)*                             | from scipy import special                                                                                   |\r\n|                                                                  | special.jn(n,x)                                                                                             |\r\n|                                                                  |                                                                                                             |\r\n|                                                                  |\\ *Note arg order is swapped*                                                                                |\r\n+------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------+\r\n|| **BESSELK**\\ ( x, n )                                           |::                                                                                                           |\r\n|                                                                  |                                                                                                             |\r\n|\\ *Returns the modified Bessel function Kn(x)*                    | from scipy import special                                                                                   |\r\n|                                                                  | special.kn(n,x)                                                                                             |\r\n|                                                                  |                                                                                                             |\r\n|                                                                  |\\ *Note arg order is swapped*                                                                                |\r\n+------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------+\r\n|| **BESSELY**\\ ( x, n )                                           |::                                                                                                           |\r\n|                                                                  |                                                                                                             |\r\n|\\ *Returns the Bessel function Yn(x)*                             | from scipy import special                                                                                   |\r\n|                                                                  | special.yn(n,x)                                                                                             |\r\n|                                                                  |                                                                                                             |\r\n|                                                                  |\\ *Note arg order is swapped*                                                                                |\r\n+------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------+\r\n|| **BIN2DEC**\\ ( number )                                         |::                                                                                                           |\r\n|                                                                  |                                                                                                             |\r\n|\\ *Converts a binary number to decimal*                           | int( my_binary_number_as_string, 2 )                                                                        |\r\n|                                                                  |                                                                                                             |\r\n|                                                                  |\\ *Returns an int*                                                                                           |\r\n+------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------+\r\n|| **BIN2HEX**\\ ( number, places )                                 |::                                                                                                           |\r\n|                                                                  |                                                                                                             |\r\n|\\ *Converts a binary number to hexadecimal*                       | hex( int( my_binary_number_as_string, 2 ) )                                                                 |\r\n|                                                                  |                                                                                                             |\r\n|                                                                  |\\ *Returns a string*                                                                                         |\r\n+------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------+\r\n|| **BIN2OCT**\\ ( number, places )                                 |::                                                                                                           |\r\n|                                                                  |                                                                                                             |\r\n|\\ *Converts a binary number to octal*                             | oct( int( my_binary_number_as_string, 2 ) )                                                                 |\r\n|                                                                  |                                                                                                             |\r\n|                                                                  |\\ *Returns a string*                                                                                         |\r\n+------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------+\r\n|| **COMPLEX**\\ ( real_num, i_num, suffix )                        |::                                                                                                           |\r\n|                                                                  |                                                                                                             |\r\n|\\ *Converts real and imaginary coefficients into a complex number*| complex(real_num, i_num)                                                                                    |\r\n|                                                                  |                                                                                                             |\r\n|                                                                  |or::                                                                                                         |\r\n|                                                                  |                                                                                                             |\r\n|                                                                  | (X + Yj)                                                                                                    |\r\n|                                                                  |                                                                                                             |\r\n|                                                                  |where X and Y are both integer or float literals.                                                            |\r\n|                                                                  |                                                                                                             |\r\n|                                                                  |\\ *Does not handle 'suffix' parameter, always uses 'j'.*                                                     |\r\n+------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------+\r\n|| **CONVERT**\\ ( number, from_unit, to_unit )                     |\\ *scipy.constants contains several constants that may be used in unit conversion*                           |\r\n|                                                                  |                                                                                                             |\r\n|\\ *Converts a number from one measurement system to another*      |                                                                                                             |\r\n+------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------+\r\n|| **DEC2BIN**\\ ( number, places )                                 |::                                                                                                           |\r\n|                                                                  |                                                                                                             |\r\n|\\ *Converts a decimal number to binary*                           | bin( number )[2:].zfill( places )                                                                           |\r\n|                                                                  |                                                                                                             |\r\n|                                                                  |\\ *bin(n) without the slice returns a string prepended with '0b', whereas excel just returns raw binary.*    |\r\n+------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------+\r\n|| **DEC2HEX**\\ ( number, places )                                 |::                                                                                                           |\r\n|                                                                  |                                                                                                             |\r\n|\\ *Converts a decimal number to hexadecimal*                      | hex( number )[2:].zfill( places ).upper()                                                                   |\r\n|                                                                  |                                                                                                             |\r\n|                                                                  |\\ *hex(n) without the slice returns a string prepended with '0x', and A-F are represented as a-f (lowercase)*|\r\n+------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------+\r\n|| **DEC2OCT**\\ ( number,  places )                                |::                                                                                                           |\r\n|                                                                  |                                                                                                             |\r\n|\\ *Converts a decimal number to octal*                            | oct( number ).zfill(places)                                                                                 |\r\n|                                                                  |                                                                                                             |\r\n|                                                                  |\\ *oct(n) prepends with a 0*                                                                                 |\r\n+------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------+\r\n|| **DELTA**\\ ( number1, number2 )                                 |::                                                                                                           |\r\n|                                                                  |                                                                                                             |\r\n|\\ *Tests whether two values are equal*                            | number1 == number2                                                                                          |\r\n|                                                                  |                                                                                                             |\r\n|                                                                  |\\ *If you require the result to be 0 or 1, use  '(number1 == number2) and 1 or 0'*                           |\r\n+------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------+\r\n|| **ERF**\\ ( lower_limit, upper_limit )                           |::                                                                                                           |\r\n|                                                                  |                                                                                                             |\r\n|\\ *Returns the error function*                                    | from scipy import special                                                                                   |\r\n|                                                                  | special.erf(x)                                                                                              |\r\n|                                                                  |                                                                                                             |\r\n|                                                                  |\\ *NB. scipy version takes complex number as only parameter. not equivalent*                                 |\r\n+------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------+\r\n|| **ERFC**\\ ( x )                                                 |::                                                                                                           |\r\n|                                                                  |                                                                                                             |\r\n|\\ *Returns the complementary error function*                      | from scipy import special                                                                                   |\r\n|                                                                  | special.erfc(x)                                                                                             |\r\n|                                                                  |                                                                                                             |\r\n|                                                                  |\\ *NB. scipy version takes complex number as only parameter. not equivalent*                                 |\r\n+------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------+\r\n|| **GESTEP**\\ ( number, step )                                    |`Coming soon`_                                                                                               |\r\n|                                                                  |                                                                                                             |\r\n|\\ *Tests whether a number is greater than a threshold value*      |                                                                                                             |\r\n+------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------+\r\n|| **HEX2BIN**\\ ( number, places )                                 |`Coming soon`_                                                                                               |\r\n|                                                                  |                                                                                                             |\r\n|\\ *Converts a hexadecimal number to binary*                       |                                                                                                             |\r\n+------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------+\r\n|| **HEX2DEC**\\ ( number )                                         |`Coming soon`_                                                                                               |\r\n|                                                                  |                                                                                                             |\r\n|\\ *Converts a hexadecimal number to decimal*                      |                                                                                                             |\r\n+------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------+\r\n|| **HEX2OCT**\\ ( number, places )                                 |`Coming soon`_                                                                                               |\r\n|                                                                  |                                                                                                             |\r\n|\\ *Converts a hexadecimal number to octal*                        |                                                                                                             |\r\n+------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------+\r\n|| **IMABS**\\ ( inumber )                                          |::                                                                                                           |\r\n|                                                                  |                                                                                                             |\r\n|\\ *Returns the absolute value (modulus) of a complex number*      | abs(inumber)                                                                                                |\r\n|                                                                  |                                                                                                             |\r\n+------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------+\r\n|| **IMAGINARY**\\ ( inumber )                                      |::                                                                                                           |\r\n|                                                                  |                                                                                                             |\r\n|\\ *Returns the imaginary coefficient of a complex number*         | inumber.imag                                                                                                |\r\n|                                                                  |                                                                                                             |\r\n+------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------+\r\n|| **IMARGUMENT**\\ ( inumber )                                     |`Coming soon`_                                                                                               |\r\n|                                                                  |                                                                                                             |\r\n|\\ *Returns the argument theta, an angle expressed in radians*     |                                                                                                             |\r\n+------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------+\r\n|| **IMCONJUGATE**\\ ( inumber )                                    |`Coming soon`_                                                                                               |\r\n|                                                                  |                                                                                                             |\r\n|\\ *Returns the complex conjugate of a complex number*             |                                                                                                             |\r\n+------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------+\r\n|| **IMCOS**\\ ( inumber )                                          |`Coming soon`_                                                                                               |\r\n|                                                                  |                                                                                                             |\r\n|\\ *Returns the cosine of a complex number*                        |                                                                                                             |\r\n+------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------+\r\n|| **IMDIV**\\ ( inumber1, inumber2 )                               |`Coming soon`_                                                                                               |\r\n|                                                                  |                                                                                                             |\r\n|\\ *Returns the quotient of two complex numbers*                   |                                                                                                             |\r\n+------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------+\r\n|| **IMEXP**\\ ( inumber )                                          |`Coming soon`_                                                                                               |\r\n|                                                                  |                                                                                                             |\r\n|\\ *Returns the exponential of a complex number*                   |                                                                                                             |\r\n+------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------+\r\n|| **IMLN**\\ ( inumber )                                           |`Coming soon`_                                                                                               |\r\n|                                                                  |                                                                                                             |\r\n|\\ *Returns the natural logarithm of a complex number*             |                                                                                                             |\r\n+------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------+\r\n|| **IMLOG10**\\ ( inumber )                                        |`Coming soon`_                                                                                               |\r\n|                                                                  |                                                                                                             |\r\n|\\ *Returns the base-10 logarithm of a complex number*             |                                                                                                             |\r\n+------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------+\r\n|| **IMLOG2**\\ ( inumber )                                         |`Coming soon`_                                                                                               |\r\n|                                                                  |                                                                                                             |\r\n|\\ *Returns the base-2 logarithm of a complex number*              |                                                                                                             |\r\n+------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------+\r\n|| **IMPOWER**\\ ( inumber, number )                                |`Coming soon`_                                                                                               |\r\n|                                                                  |                                                                                                             |\r\n|\\ *Returns a complex number raised to an integer power*           |                                                                                                             |\r\n+------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------+\r\n|| **IMPRODUCT**\\ ( inumber1, inumber2, ... )                      |`Coming soon`_                                                                                               |\r\n|                                                                  |                                                                                                             |\r\n|\\ *Returns the product of complex numbers*                        |                                                                                                             |\r\n+------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------+\r\n|| **IMREAL**\\ ( inumber )                                         |`Coming soon`_                                                                                               |\r\n|                                                                  |                                                                                                             |\r\n|\\ *Returns the real coefficient of a complex number*              |                                                                                                             |\r\n+------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------+\r\n|| **IMSIN**\\ ( inumber )                                          |`Coming soon`_                                                                                               |\r\n|                                                                  |                                                                                                             |\r\n|\\ *Returns the sine of a complex number*                          |                                                                                                             |\r\n+------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------+\r\n|| **IMSQRT**\\ ( inumber )                                         |`Coming soon`_                                                                                               |\r\n|                                                                  |                                                                                                             |\r\n|\\ *Returns the square root of a complex number*                   |                                                                                                             |\r\n+------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------+\r\n|| **IMSUB**\\ ( inumber1, inumber2 )                               |`Coming soon`_                                                                                               |\r\n|                                                                  |                                                                                                             |\r\n|\\ *Returns the difference between two complex numbers*            |                                                                                                             |\r\n+------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------+\r\n|| **IMSUM**\\ ( inumber1, inumber2, ... )                          |`Coming soon`_                                                                                               |\r\n|                                                                  |                                                                                                             |\r\n|\\ *Returns the sum of complex numbers*                            |                                                                                                             |\r\n+------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------+\r\n|| **OCT2BIN**\\ ( number, places )                                 |`Coming soon`_                                                                                               |\r\n|                                                                  |                                                                                                             |\r\n|\\ *Converts an octal number to binary*                            |                                                                                                             |\r\n+------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------+\r\n|| **OCT2DEC**\\ ( number )                                         |`Coming soon`_                                                                                               |\r\n|                                                                  |                                                                                                             |\r\n|\\ *Converts an octal number to decimal*                           |                                                                                                             |\r\n+------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------+\r\n|| **OCT2HEX**\\ ( number, places )                                 |`Coming soon`_                                                                                               |\r\n|                                                                  |                                                                                                             |\r\n|\\ *Converts an octal number to hexadecimal*                       |                                                                                                             |\r\n+------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------+\r\n\r\nFinancial functions\r\n-------------------\r\n\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|Function                                                                                                                                          |Python Equivalent|\r\n+==================================================================================================================================================+=================+\r\n|| **ACCRINT**\\ ( issue, first_interest, settlement, rate, par, frequency, basis )                                                                 |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the accrued interest for a security that pays periodic interest*                                                                       |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **ACCRINTM**\\ ( issue, settlement, rate, par, basis )                                                                                           |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the accrued interest for a security that pays interest at maturity*                                                                    |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **AMORDEGRC**\\ ( cost, date_purchased, first_period, salvage, period, rate, basis )                                                             |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the depreciation for each accounting period by using a depreciation coefficient*                                                       |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **AMORLINC**\\ ( cost, date_purchased, first_period, salvage, period, rate, basis )                                                              |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the depreciation for each accounting period*                                                                                           |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **COUPDAYBS**\\ ( settlement, maturity, frequency, basis )                                                                                       |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the number of days from the beginning of the coupon period to the settlement date*                                                     |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **COUPDAYS**\\ ( settlement, maturity, frequency, basis )                                                                                        |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the number of days in the coupon period that contains the settlement date*                                                             |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **COUPDAYSNC**\\ ( settlement, maturity, frequency, basis )                                                                                      |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the number of days from the settlement date to the next coupon date*                                                                   |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **COUPNCD**\\ ( settlement, maturity, frequency, basis )                                                                                         |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the next coupon date after the settlement date*                                                                                        |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **COUPNUM**\\ ( settlement, maturity, frequency, basis )                                                                                         |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the number of coupons payable between the settlement date and maturity date*                                                           |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **COUPPCD**\\ ( settlement, maturity, frequency, basis )                                                                                         |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the previous coupon date before the settlement date*                                                                                   |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **CUMIPMT**\\ ( rate, nper, pv, start_period, end_period, type )                                                                                 |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the cumulative interest paid between two periods*                                                                                      |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **CUMPRINC**\\ ( rate, nper, pv, start_period, end_period, type )                                                                                |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the cumulative principal paid on a loan between two periods*                                                                           |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **DB**\\ ( cost, salvage, life, period, month )                                                                                                  |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the depreciation of an asset for a specified period by using the fixed-declining balance method*                                       |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **DDB**\\ ( cost, salvage, life, period, factor )                                                                                                |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the depreciation of an asset for a specified period by using the double-declining balance method or some other method that you specify*|                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **DISC**\\ ( settlement, maturity, pr, redemption, basis )                                                                                       |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the discount rate for a security*                                                                                                      |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **DOLLARDE**\\ ( fractional_dollar, fraction )                                                                                                   |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Converts a dollar price, expressed as a fraction, into a dollar price, expressed as a decimal number*                                          |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **DOLLARFR**\\ ( decimal_dollar, fraction )                                                                                                      |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Converts a dollar price, expressed as a decimal number, into a dollar price, expressed as a fraction*                                          |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **DURATION**\\ ( settlement, maturity, coupon, yld, frequency, basis )                                                                           |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the annual duration of a security with periodic interest payments*                                                                     |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **EFFECT**\\ ( nominal_rate, npery )                                                                                                             |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the effective annual interest rate*                                                                                                    |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **FV**\\ ( rate, nper, pmt, pv, type )                                                                                                           |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the future value of an investment*                                                                                                     |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **FVSCHEDULE**\\ ( principal, schedule )                                                                                                         |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the future value of an initial principal after applying a series of compound interest rates*                                           |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **INTRATE**\\ ( settlement, maturity, investment, redemption, basis )                                                                            |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the interest rate for a fully invested security*                                                                                       |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **IPMT**\\ ( rate, per, nper, pv, fv, type )                                                                                                     |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the interest payment for an investment for a given period*                                                                             |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **IRR**\\ ( values, guess )                                                                                                                      |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the internal rate of return for a series of cash flows*                                                                                |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **ISPMT**\\ (  )                                                                                                                                 |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Calculates the interest paid during a specific period of an investment*                                                                        |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **MDURATION**\\ ( settlement, maturity, coupon, yld, frequency, basis )                                                                          |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the Macauley modified duration for a security with an assumed par value of $100*                                                       |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **MIRR**\\ ( values, finance_rate, reinvest_rate )                                                                                               |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the internal rate of return where positive and negative cash flows are financed at different rates*                                    |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **NOMINAL**\\ ( effect_rate, npery )                                                                                                             |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the annual nominal interest rate*                                                                                                      |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **NPER**\\ ( rate,  pmt,  pv,  fv,  type )                                                                                                       |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the number of periods for an investment*                                                                                               |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **NPV**\\ ( rate, value1, value2,  ... )                                                                                                         |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the net present value of an investment based on a series of periodic cash flows and a discount rate*                                   |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **ODDFPRICE**\\ ( settlement, maturity, issue, first_coupon, rate, yld, redemption, frequency, basis )                                           |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the price per $100 face value of a security with an odd first period*                                                                  |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **ODDFYIELD**\\ ( settlement, maturity, issue, first_coupon, rate, pr, redemption, frequency, basis )                                            |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the yield of a security with an odd first period*                                                                                      |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **ODDLPRICE**\\ ( settlement, maturity, last_interest, rate, yld, redemption, frequency, basis )                                                 |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the price per $100 face value of a security with an odd last period*                                                                   |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **ODDLYIELD**\\ ( settlement, maturity, last_interest, rate, pr, redemption, frequency, basis )                                                  |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the yield of a security with an odd last period*                                                                                       |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **PMT**\\ ( rate, nper, pv, fv, type )                                                                                                           |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the periodic payment for an annuity*                                                                                                   |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **PPMT**\\ ( rate, per, nper, pv, fv, type )                                                                                                     |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the payment on the principal for an investment for a given period*                                                                     |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **PRICE**\\ ( settlement, maturity, rate, yld, redemption, frequency, basis )                                                                    |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the price per $100 face value of a security that pays periodic interest*                                                               |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **PRICEDISC**\\ ( settlement, maturity, discount, redemption, basis )                                                                            |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the price per $100 face value of a discounted security*                                                                                |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **PRICEMAT**\\ ( settlement, maturity, issue, rate, yld, basis )                                                                                 |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the price per $100 face value of a security that pays interest at maturity*                                                            |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **PV**\\ ( rate, nper, pmt, fv, type )                                                                                                           |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the present value of an investment*                                                                                                    |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **RATE**\\ ( nper, pmt, pv, fv, type, guess )                                                                                                    |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the interest rate per period of an annuity*                                                                                            |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **RECEIVED**\\ ( settlement, maturity, investment, discount, basis )                                                                             |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the amount received at maturity for a fully invested security*                                                                         |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **SLN**\\ ( cost, salvage, life )                                                                                                                |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the straight-line depreciation of an asset for one period*                                                                             |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **SYD**\\ ( cost, salvage, life, per )                                                                                                           |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the sum-of-years' digits depreciation of an asset for a specified period*                                                              |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **TBILLEQ**\\ ( settlement, maturity, discount )                                                                                                 |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the bond-equivalent yield for a Treasury bill*                                                                                         |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **TBILLPRICE**\\ ( settlement, maturity, discount )                                                                                              |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the price per $100 face value for a Treasury bill*                                                                                     |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **TBILLYIELD**\\ ( settlement, maturity, pr )                                                                                                    |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the yield for a Treasury bill*                                                                                                         |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **VDB**\\ ( cost, salvage, life, start_period, end_period, factor, no_switch )                                                                   |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the depreciation of an asset for a specified or partial period by using a declining balance method*                                    |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **XIRR**\\ ( values, dates, guess )                                                                                                              |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the internal rate of return for a schedule of cash flows that is not necessarily periodic*                                             |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **XNPV**\\ ( rate, values, dates )                                                                                                               |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the net present value for a schedule of cash flows that is not necessarily periodic*                                                   |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **YIELD**\\ ( settlement, maturity, rate, pr, redemption, frequency, basis )                                                                     |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the yield on a security that pays periodic interest*                                                                                   |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **YIELDDISC**\\ ( settlement, maturity, pr, redemption, basis )                                                                                  |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the annual yield for a discounted security; for example, a Treasury bill*                                                              |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n|| **YIELDMAT**\\ ( settlement, maturity, issue, rate, pr, basis )                                                                                  |`Coming soon`_   |\r\n|                                                                                                                                                  |                 |\r\n|\\ *Returns the annual yield of a security that pays interest at maturity*                                                                         |                 |\r\n+--------------------------------------------------------------------------------------------------------------------------------------------------+-----------------+\r\n\r\nInformation functions\r\n---------------------\r\n\r\n+-----------------------------------------------------------------------------+---------------------------------------------------+\r\n|Function                                                                     |Python Equivalent                                  |\r\n+=============================================================================+===================================================+\r\n|| **CELL**\\ ( info_type, reference )                                         |`Coming soon`_                                     |\r\n|                                                                             |                                                   |\r\n|\\ *Returns information about the formatting, location, or contents of a cell*|                                                   |\r\n+-----------------------------------------------------------------------------+---------------------------------------------------+\r\n|| **ERROR.TYPE**\\ ( error_val )                                              |`Coming soon`_                                     |\r\n|                                                                             |                                                   |\r\n|\\ *Returns a number corresponding to an error type*                          |                                                   |\r\n+-----------------------------------------------------------------------------+---------------------------------------------------+\r\n|| **INFO**\\ ( type_text )                                                    |`Coming soon`_                                     |\r\n|                                                                             |                                                   |\r\n|\\ *Returns information about the current operating environment*              |                                                   |\r\n+-----------------------------------------------------------------------------+---------------------------------------------------+\r\n|| **ISBLANK**\\ (  )                                                          |`Coming soon`_                                     |\r\n|                                                                             |                                                   |\r\n|\\ *Returns TRUE if the value is blank*                                       |                                                   |\r\n+-----------------------------------------------------------------------------+---------------------------------------------------+\r\n|| **ISERR**\\ (  )                                                            |`Coming soon`_                                     |\r\n|                                                                             |                                                   |\r\n|\\ *Returns TRUE if the value is any error value except #N/A*                 |                                                   |\r\n+-----------------------------------------------------------------------------+---------------------------------------------------+\r\n|| **ISERROR**\\ (  )                                                          |`Coming soon`_                                     |\r\n|                                                                             |                                                   |\r\n|\\ *Returns TRUE if the value is any error value*                             |                                                   |\r\n+-----------------------------------------------------------------------------+---------------------------------------------------+\r\n|| **ISEVEN**\\ ( number )                                                     |::                                                 |\r\n|                                                                             |                                                   |\r\n|\\ *Returns TRUE if the number is even*                                       | number % 2 == 0                                   |\r\n|                                                                             |                                                   |\r\n+-----------------------------------------------------------------------------+---------------------------------------------------+\r\n|| **ISLOGICAL**\\ (  )                                                        |::                                                 |\r\n|                                                                             |                                                   |\r\n|\\ *Returns TRUE if the value is a logical value*                             | isinstance(value, bool)                           |\r\n|                                                                             |                                                   |\r\n+-----------------------------------------------------------------------------+---------------------------------------------------+\r\n|| **ISNA**\\ (  )                                                             |`Coming soon`_                                     |\r\n|                                                                             |                                                   |\r\n|\\ *Returns TRUE if the value is the #N/A error value*                        |                                                   |\r\n+-----------------------------------------------------------------------------+---------------------------------------------------+\r\n|| **ISNONTEXT**\\ (  )                                                        |::                                                 |\r\n|                                                                             |                                                   |\r\n|\\ *Returns TRUE if the value is not text*                                    | not isinstance(value, basestring)                 |\r\n|                                                                             |                                                   |\r\n+-----------------------------------------------------------------------------+---------------------------------------------------+\r\n|| **ISNUMBER**\\ (  )                                                         |`Coming soon`_                                     |\r\n|                                                                             |                                                   |\r\n|\\ *Returns TRUE if the value is a number*                                    |                                                   |\r\n+-----------------------------------------------------------------------------+---------------------------------------------------+\r\n|| **ISODD**\\ ( number )                                                      |::                                                 |\r\n|                                                                             |                                                   |\r\n|\\ *Returns TRUE if the number is odd*                                        | number % 2 == 1                                   |\r\n|                                                                             |                                                   |\r\n+-----------------------------------------------------------------------------+---------------------------------------------------+\r\n|| **ISREF**\\ (  )                                                            |`Coming soon`_                                     |\r\n|                                                                             |                                                   |\r\n|\\ *Returns TRUE if the value is a reference*                                 |                                                   |\r\n+-----------------------------------------------------------------------------+---------------------------------------------------+\r\n|| **ISTEXT**\\ (  )                                                           |::                                                 |\r\n|                                                                             |                                                   |\r\n|\\ *Returns TRUE if the value is text*                                        | isinstance(value, basestring)                     |\r\n|                                                                             |                                                   |\r\n+-----------------------------------------------------------------------------+---------------------------------------------------+\r\n|| **N**\\ ( value )                                                           |::                                                 |\r\n|                                                                             |                                                   |\r\n|\\ *Returns a value converted to a number*                                    | float(value)                                      |\r\n|                                                                             |                                                   |\r\n+-----------------------------------------------------------------------------+---------------------------------------------------+\r\n|| **NA**\\ (   )                                                              |`Coming soon`_                                     |\r\n|                                                                             |                                                   |\r\n|\\ *Returns the error value #N/A*                                             |                                                   |\r\n+-----------------------------------------------------------------------------+---------------------------------------------------+\r\n|| **TYPE**\\ ( value )                                                        |::                                                 |\r\n|                                                                             |                                                   |\r\n|\\ *Returns a number indicating the data type of a value*                     | type(value)                                       |\r\n|                                                                             |                                                   |\r\n|                                                                             |\\ *Returns a Python type though, not an Excel type*|\r\n+-----------------------------------------------------------------------------+---------------------------------------------------+\r\n\r\nLogical functions\r\n-----------------\r\n\r\n+----------------------------------------------------------------------------------------------------------------+-------------------------------------------------+\r\n|Function                                                                                                        |Python Equivalent                                |\r\n+================================================================================================================+=================================================+\r\n|| **AND**\\ ( logical1, logical2,  ... )                                                                         |::                                               |\r\n|                                                                                                                |                                                 |\r\n|\\ *Returns TRUE if all of its arguments are TRUE*                                                               | logical1 and logical2 and logical3...           |\r\n|                                                                                                                |                                                 |\r\n+----------------------------------------------------------------------------------------------------------------+-------------------------------------------------+\r\n|| **FALSE**\\ (  )                                                                                               |::                                               |\r\n|                                                                                                                |                                                 |\r\n|\\ *Returns the logical value FALSE*                                                                             | False                                           |\r\n|                                                                                                                |                                                 |\r\n+----------------------------------------------------------------------------------------------------------------+-------------------------------------------------+\r\n|| **IF**\\ ( logical_test, value_if_true, value_if_false )                                                       |::                                               |\r\n|                                                                                                                |                                                 |\r\n|\\ *Specifies a logical test to perform*                                                                         | logical_test and value_if_true or value_if_false|\r\n|                                                                                                                |                                                 |\r\n|                                                                                                                |\\ *known as the 'and/or trick'*                  |\r\n+----------------------------------------------------------------------------------------------------------------+-------------------------------------------------+\r\n|| **IFERROR**\\ (  )                                                                                             |`Coming soon`_                                   |\r\n|                                                                                                                |                                                 |\r\n|\\ *Returns a value you specify if a formula evaluates to an error; otherwise, returns the result of the formula*|                                                 |\r\n+----------------------------------------------------------------------------------------------------------------+-------------------------------------------------+\r\n|| **NOT**\\ ( logical )                                                                                          |::                                               |\r\n|                                                                                                                |                                                 |\r\n|\\ *Reverses the logic of its argument*                                                                          | not arg                                         |\r\n|                                                                                                                |                                                 |\r\n+----------------------------------------------------------------------------------------------------------------+-------------------------------------------------+\r\n|| **OR**\\ ( logical1, logical2, ... )                                                                           |::                                               |\r\n|                                                                                                                |                                                 |\r\n|\\ *Returns TRUE if any argument is TRUE*                                                                        | logical1 or logical2 or logical3...             |\r\n|                                                                                                                |                                                 |\r\n+----------------------------------------------------------------------------------------------------------------+-------------------------------------------------+\r\n|| **TRUE**\\ (  )                                                                                                |::                                               |\r\n|                                                                                                                |                                                 |\r\n|\\ *Returns the logical value TRUE*                                                                              | True                                            |\r\n|                                                                                                                |                                                 |\r\n+----------------------------------------------------------------------------------------------------------------+-------------------------------------------------+\r\n\r\nLookup and reference functions\r\n------------------------------\r\n\r\n+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------+\r\n|Function                                                                                                                                                                                                                                                                                              |Python Equivalent                   |\r\n+======================================================================================================================================================================================================================================================================================================+====================================+\r\n|| **ADDRESS**\\ ( row_num, column_num, abs_num, a1, sheet_text )                                                                                                                                                                                                                                       |`Coming soon`_                      |\r\n|                                                                                                                                                                                                                                                                                                      |                                    |\r\n|\\ *Returns a reference as text to a single cell in a worksheet*                                                                                                                                                                                                                                       |                                    |\r\n+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------+\r\n|| **AREAS**\\ ( reference )                                                                                                                                                                                                                                                                            |`Coming soon`_                      |\r\n|                                                                                                                                                                                                                                                                                                      |                                    |\r\n|\\ *Returns the number of areas in a reference*                                                                                                                                                                                                                                                        |                                    |\r\n+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------+\r\n|| **CHOOSE**\\ ( index_num, value1, value2, ... )                                                                                                                                                                                                                                                      |::                                  |\r\n|                                                                                                                                                                                                                                                                                                      |                                    |\r\n|\\ *Chooses a value from a list of values*                                                                                                                                                                                                                                                             | [value1, value2...][index_num]     |\r\n|                                                                                                                                                                                                                                                                                                      |                                    |\r\n+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------+\r\n|| **COLUMN**\\ ( reference )                                                                                                                                                                                                                                                                           |`Coming soon`_                      |\r\n|                                                                                                                                                                                                                                                                                                      |                                    |\r\n|\\ *Returns the column number of a reference*                                                                                                                                                                                                                                                          |                                    |\r\n+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------+\r\n|| **COLUMNS**\\ ( array )                                                                                                                                                                                                                                                                              |`Coming soon`_                      |\r\n|                                                                                                                                                                                                                                                                                                      |                                    |\r\n|\\ *Returns the number of columns in a reference*                                                                                                                                                                                                                                                      |                                    |\r\n+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------+\r\n|| **HLOOKUP**\\ ( lookup_value, table_array, row_index_num, range_lookup )                                                                                                                                                                                                                             |`Coming soon`_                      |\r\n|                                                                                                                                                                                                                                                                                                      |                                    |\r\n|\\ *Looks in the top row of an array and returns the value of the indicated cell*                                                                                                                                                                                                                      |                                    |\r\n+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------+\r\n|| **HYPERLINK**\\ ( link_location, friendly_name )                                                                                                                                                                                                                                                     |`Coming soon`_                      |\r\n|                                                                                                                                                                                                                                                                                                      |                                    |\r\n|\\ *Creates a shortcut or jump that opens a document stored on a network server, an intranet, or the Internet*                                                                                                                                                                                         |                                    |\r\n+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------+\r\n|| **INDEX**\\ ( array, row_num, column_num )                                                                                                                                                                                                                                                           |`Coming soon`_                      |\r\n|                                                                                                                                                                                                                                                                                                      |                                    |\r\n|\\ *Uses an index to choose a value from a reference or array*                                                                                                                                                                                                                                         |                                    |\r\n+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------+\r\n|| **INDIRECT**\\ ( ref_text, a1 )                                                                                                                                                                                                                                                                      |`Coming soon`_                      |\r\n|                                                                                                                                                                                                                                                                                                      |                                    |\r\n|\\ *Returns a reference indicated by a text value*                                                                                                                                                                                                                                                     |                                    |\r\n+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------+\r\n|| **LOOKUP**\\ ( lookup_value, lookup_vector, result_vector )                                                                                                                                                                                                                                          |`Coming soon`_                      |\r\n|                                                                                                                                                                                                                                                                                                      |                                    |\r\n|\\ *Looks up values in a vector or array*                                                                                                                                                                                                                                                              |                                    |\r\n+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------+\r\n|| **MATCH**\\ ( lookup_value, lookup_array, match_type )                                                                                                                                                                                                                                               |::                                  |\r\n|                                                                                                                                                                                                                                                                                                      |                                    |\r\n|\\ *Looks up values in a reference or array*                                                                                                                                                                                                                                                           | lookup_array.index( lookup_value ) |\r\n|                                                                                                                                                                                                                                                                                                      |                                    |\r\n|                                                                                                                                                                                                                                                                                                      |\\ *Only handles match type 'equals'*|\r\n+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------+\r\n|| **OFFSET**\\ ( reference, rows, cols, height, width )                                                                                                                                                                                                                                                |`Coming soon`_                      |\r\n|                                                                                                                                                                                                                                                                                                      |                                    |\r\n|\\ *Returns a reference offset from a given reference*                                                                                                                                                                                                                                                 |                                    |\r\n+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------+\r\n|| **ROW**\\ ( reference )                                                                                                                                                                                                                                                                              |`Coming soon`_                      |\r\n|                                                                                                                                                                                                                                                                                                      |                                    |\r\n|\\ *Returns the row number of a reference*                                                                                                                                                                                                                                                             |                                    |\r\n+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------+\r\n|| **ROWS**\\ ( array )                                                                                                                                                                                                                                                                                 |`Coming soon`_                      |\r\n|                                                                                                                                                                                                                                                                                                      |                                    |\r\n|\\ *Returns the number of rows in a reference*                                                                                                                                                                                                                                                         |                                    |\r\n+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------+\r\n|| **RTD**\\ ( ProgID, server, topic1, [topic2], ... )                                                                                                                                                                                                                                                  |`Coming soon`_                      |\r\n|                                                                                                                                                                                                                                                                                                      |                                    |\r\n|\\ *Retrieves real-time data from a program that supports COM automation (Automation: A way to work with an application's objects from another application or development tool. Formerly called OLE Automation, Automation is an industry standard and a feature of the Component Object Model (COM).)*|                                    |\r\n+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------+\r\n|| **TRANSPOSE**\\ ( array )                                                                                                                                                                                                                                                                            |`Coming soon`_                      |\r\n|                                                                                                                                                                                                                                                                                                      |                                    |\r\n|\\ *Returns the transpose of an array*                                                                                                                                                                                                                                                                 |                                    |\r\n+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------+\r\n|| **VLOOKUP**\\ ( lookup_value, table_array, col_index_num, range_lookup )                                                                                                                                                                                                                             |`Coming soon`_                      |\r\n|                                                                                                                                                                                                                                                                                                      |                                    |\r\n|\\ *Looks in the first column of an array and moves across the row to return the value of a cell*                                                                                                                                                                                                      |                                    |\r\n+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------+\r\n\r\nDatabase\r\n--------\r\n\r\n+-------------------------------------------------------------------------------------------------+-----------------+\r\n|Function                                                                                         |Python Equivalent|\r\n+=================================================================================================+=================+\r\n|| **DAVERAGE**\\ ( database, field, criteria )                                                    |`Coming soon`_   |\r\n|                                                                                                 |                 |\r\n|\\ *Returns the average of selected database entries*                                             |                 |\r\n+-------------------------------------------------------------------------------------------------+-----------------+\r\n|| **DCOUNT**\\ ( database, field, criteria )                                                      |`Coming soon`_   |\r\n|                                                                                                 |                 |\r\n|\\ *Counts the cells that contain numbers in a database*                                          |                 |\r\n+-------------------------------------------------------------------------------------------------+-----------------+\r\n|| **DCOUNTA**\\ ( database, field, criteria )                                                     |`Coming soon`_   |\r\n|                                                                                                 |                 |\r\n|\\ *Counts nonblank cells in a database*                                                          |                 |\r\n+-------------------------------------------------------------------------------------------------+-----------------+\r\n|| **DGET**\\ ( database, field, criteria )                                                        |`Coming soon`_   |\r\n|                                                                                                 |                 |\r\n|\\ *Extracts from a database a single record that matches the specified criteria*                 |                 |\r\n+-------------------------------------------------------------------------------------------------+-----------------+\r\n|| **DMAX**\\ ( database, field, criteria )                                                        |`Coming soon`_   |\r\n|                                                                                                 |                 |\r\n|\\ *Returns the maximum value from selected database entries*                                     |                 |\r\n+-------------------------------------------------------------------------------------------------+-----------------+\r\n|| **DMIN**\\ ( database, field, criteria )                                                        |`Coming soon`_   |\r\n|                                                                                                 |                 |\r\n|\\ *Returns the minimum value from selected database entries*                                     |                 |\r\n+-------------------------------------------------------------------------------------------------+-----------------+\r\n|| **DPRODUCT**\\ ( database, field, criteria )                                                    |`Coming soon`_   |\r\n|                                                                                                 |                 |\r\n|\\ *Multiplies the values in a particular field of records that match the criteria in a database* |                 |\r\n+-------------------------------------------------------------------------------------------------+-----------------+\r\n|| **DSTDEV**\\ ( database, field, criteria )                                                      |`Coming soon`_   |\r\n|                                                                                                 |                 |\r\n|\\ *Estimates the standard deviation based on a sample of selected database entries*              |                 |\r\n+-------------------------------------------------------------------------------------------------+-----------------+\r\n|| **DSTDEVP**\\ ( database, field, criteria )                                                     |`Coming soon`_   |\r\n|                                                                                                 |                 |\r\n|\\ *Calculates the standard deviation based on the entire population of selected database entries*|                 |\r\n+-------------------------------------------------------------------------------------------------+-----------------+\r\n|| **DSUM**\\ ( database, field, criteria )                                                        |`Coming soon`_   |\r\n|                                                                                                 |                 |\r\n|\\ *Adds the numbers in the field column of records in the database that match the criteria*      |                 |\r\n+-------------------------------------------------------------------------------------------------+-----------------+\r\n|| **DVAR**\\ ( database, field, criteria )                                                        |`Coming soon`_   |\r\n|                                                                                                 |                 |\r\n|\\ *Estimates variance based on a sample from selected database entries*                          |                 |\r\n+-------------------------------------------------------------------------------------------------+-----------------+\r\n|| **DVARP**\\ ( database, field, criteria )                                                       |`Coming soon`_   |\r\n|                                                                                                 |                 |\r\n|\\ *Calculates variance based on the entire population of selected database entries*              |                 |\r\n+-------------------------------------------------------------------------------------------------+-----------------+\r\n\r\nStatistical functions\r\n---------------------\r\n\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|Function                                                                                                                |Python Equivalent                                      |\r\n+========================================================================================================================+=======================================================+\r\n|| **AVEDEV**\\ ( number1, number2, ... )                                                                                 |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the average of the absolute deviations of data points from their mean*                                       |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **AVERAGE**\\ ( number1, number2, ... )                                                                                |::                                                     |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the average of its arguments*                                                                                | values = [number1, number2...]                        |\r\n|                                                                                                                        | average = sum(values) / len(values)                   |\r\n|                                                                                                                        |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **AVERAGEA**\\ ( value1, value2, ... )                                                                                 |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the average of its arguments, including numbers, text, and logical values*                                   |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **AVERAGEIF**\\ (  )                                                                                                   |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the average (arithmetic mean) of all the cells in a range that meet a given criteria*                        |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **AVERAGEIFS**\\ (  )                                                                                                  |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the average (arithmetic mean) of all cells that meet multiple criteria.*                                     |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **BETADIST**\\ ( x, alpha, beta, A, B )                                                                                |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the beta cumulative distribution function*                                                                   |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **BETAINV**\\ ( probability, alpha, beta, A, B )                                                                       |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the inverse of the cumulative distribution function for a specified beta distribution*                       |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **BINOMDIST**\\ ( number_s, trials, probability_s, cumulative )                                                        |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the individual term binomial distribution probability*                                                       |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **CHIDIST**\\ ( x, degrees_freedom )                                                                                   |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the one-tailed probability of the chi-squared distribution*                                                  |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **CHIINV**\\ ( probability, degrees_freedom )                                                                          |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the inverse of the one-tailed probability of the chi-squared distribution*                                   |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **CHITEST**\\ ( actual_range, expected_range )                                                                         |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the test for independence*                                                                                   |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **CONFIDENCE**\\ ( alpha, standard_dev, size )                                                                         |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the confidence interval for a population mean*                                                               |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **CORREL**\\ ( array1, array2 )                                                                                        |\\ *See numpy.corrcoef*                                 |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the correlation coefficient between two data sets*                                                           |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **COUNT**\\ ( value1, value2, ... )                                                                                    |::                                                     |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Counts how many numbers are in the list of arguments*                                                                | sum(1 for a in [value1, value2] if isinstance(a, int))|\r\n|                                                                                                                        |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **COUNTA**\\ ( value1, value2, ... )                                                                                   |::                                                     |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Counts how many values are in the list of arguments*                                                                 | len([value1, value2])                                 |\r\n|                                                                                                                        |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **COUNTBLANK**\\ ( range )                                                                                             |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Counts the number of blank cells within a range*                                                                     |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **COUNTIF**\\ ( range, criteria )                                                                                      |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Counts the number of cells within a range that meet the given criteria*                                              |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **COUNTIFS**\\ (  )                                                                                                    |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Counts the number of cells within a range that meet multiple criteria*                                               |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **COVAR**\\ ( array1, array2 )                                                                                         |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns covariance, the average of the products of paired deviations*                                                |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **CRITBINOM**\\ ( trials, probability_s, alpha )                                                                       |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the smallest value for which the cumulative binomial distribution is less than or equal to a criterion value*|                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **DEVSQ**\\ ( number1, number2, ... )                                                                                  |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the sum of squares of deviations*                                                                            |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **EXPONDIST**\\ ( x, lambda, cumulative )                                                                              |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the exponential distribution*                                                                                |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **FDIST**\\ ( x, degrees_freedom1, degrees_freedom2 )                                                                  |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the F probability distribution*                                                                              |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **FINV**\\ ( probability, degrees_freedom1, degrees_freedom2 )                                                         |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the inverse of the F probability distribution*                                                               |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **FISHER**\\ ( x )                                                                                                     |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the Fisher transformation*                                                                                   |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **FISHERINV**\\ ( y )                                                                                                  |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the inverse of the Fisher transformation*                                                                    |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **FORECAST**\\ ( x, known_y's, known_x's )                                                                             |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns a value along a linear trend*                                                                                |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **FREQUENCY**\\ ( data_array, bins_array )                                                                             |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns a frequency distribution as a vertical array*                                                                |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **FTEST**\\ ( array1, array2 )                                                                                         |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the result of an F-test*                                                                                     |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **GAMMADIST**\\ ( x, alpha, beta, cumulative )                                                                         |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the gamma distribution*                                                                                      |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **GAMMAINV**\\ ( probability, alpha, beta )                                                                            |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the inverse of the gamma cumulative distribution*                                                            |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **GAMMALN**\\ ( x )                                                                                                    |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the natural logarithm of the gamma function, G(x)*                                                           |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **GEOMEAN**\\ ( number1, number2, ... )                                                                                |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the geometric mean*                                                                                          |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **GROWTH**\\ ( known_y's, known_x's, new_x's, const )                                                                  |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns values along an exponential trend*                                                                           |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **HARMEAN**\\ ( number1, number2, ... )                                                                                |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the harmonic mean*                                                                                           |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **HYPGEOMDIST**\\ ( sample_s, number_sample, population_s, number_population )                                         |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the hypergeometric distribution*                                                                             |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **INTERCEPT**\\ ( known_y's, known_x's )                                                                               |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the intercept of the linear regression line*                                                                 |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **KURT**\\ ( number1, number2, ... )                                                                                   |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the kurtosis of a data set*                                                                                  |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **LARGE**\\ ( array, k )                                                                                               |::                                                     |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the k-th largest value in a data set*                                                                        | reverse( sorted( my_list )[k]                         |\r\n|                                                                                                                        |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **LINEST**\\ ( known_y's, known_x's, const, stats )                                                                    |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the parameters of a linear trend*                                                                            |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **LOGEST**\\ ( known_y's, known_x's, const, stats )                                                                    |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the parameters of an exponential trend*                                                                      |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **LOGINV**\\ ( probability, mean, standard_dev )                                                                       |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the inverse of the lognormal distribution*                                                                   |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **LOGNORMDIST**\\ ( x, mean, standard_dev )                                                                            |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the cumulative lognormal distribution*                                                                       |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **MAX**\\ ( number1, number2, ... )                                                                                    |::                                                     |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the maximum value in a list of arguments*                                                                    | max(my_list)                                          |\r\n|                                                                                                                        |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **MAXA**\\ ( value1, value2, ... )                                                                                     |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the maximum value in a list of arguments, including numbers, text, and logical values*                       |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **MEDIAN**\\ ( number1, number2, ... )                                                                                 |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the median of the given numbers*                                                                             |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **MIN**\\ ( number1, number2, ... )                                                                                    |::                                                     |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the minimum value in a list of arguments*                                                                    | min(my_list)                                          |\r\n|                                                                                                                        |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **MINA**\\ ( value1, value2, ... )                                                                                     |::                                                     |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the smallest value in a list of arguments, including numbers, text, and logical values*                      | min(my_list)                                          |\r\n|                                                                                                                        |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **MODE**\\ ( number1, number2, ... )                                                                                   |\\ *See scipy.stats.mode*                               |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the most common value in a data set*                                                                         |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **NEGBINOMDIST**\\ ( number_f, number_s, probability_s )                                                               |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the negative binomial distribution*                                                                          |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **NORMDIST**\\ ( x, mean, standard_dev, cumulative )                                                                   |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the normal cumulative distribution*                                                                          |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **NORMINV**\\ ( probability, mean, standard_dev )                                                                      |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the inverse of the normal cumulative distribution*                                                           |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **NORMSDIST**\\ ( z )                                                                                                  |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the standard normal cumulative distribution*                                                                 |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **NORMSINV**\\ ( probability )                                                                                         |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the inverse of the standard normal cumulative distribution*                                                  |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **PEARSON**\\ ( array1, array2 )                                                                                       |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the Pearson product moment correlation coefficient*                                                          |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **PERCENTILE**\\ ( array, k )                                                                                          |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the k-th percentile of values in a range*                                                                    |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **PERCENTRANK**\\ ( array, x, significance )                                                                           |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the percentage rank of a value in a data set*                                                                |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **PERMUT**\\ ( number, number_chosen )                                                                                 |::                                                     |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the number of permutations for a given number of objects*                                                    | len( itertools.permutations( my_list, r ) )           |\r\n|                                                                                                                        |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **POISSON**\\ ( x, mean, cumulative )                                                                                  |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the Poisson distribution*                                                                                    |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **PROB**\\ ( x_range, prob_range, lower_limit, upper_limit )                                                           |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the probability that values in a range are between two limits*                                               |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **QUARTILE**\\ ( array, quart )                                                                                        |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the quartile of a data set*                                                                                  |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **RANK**\\ ( number, ref, order )                                                                                      |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the rank of a number in a list of numbers*                                                                   |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **RSQ**\\ ( known_y's, known_x's )                                                                                     |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the square of the Pearson product moment correlation coefficient*                                            |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **SKEW**\\ ( number1, number2, ... )                                                                                   |\\ *See scipy.stats.skew*                               |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the skewness of a distribution*                                                                              |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **SLOPE**\\ ( known_y's, known_x's )                                                                                   |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the slope of the linear regression line*                                                                     |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **SMALL**\\ ( array, k )                                                                                               |::                                                     |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the k-th smallest value in a data set*                                                                       | sorted( my_list )[k]                                  |\r\n|                                                                                                                        |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **STANDARDIZE**\\ ( x, mean, standard_dev )                                                                            |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns a normalized value*                                                                                          |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **STDEV**\\ ( number1, number2, ... )                                                                                  |::                                                     |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Estimates standard deviation based on a sample*                                                                      | numpy.std( [number1, number2] )                       |\r\n|                                                                                                                        |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **STDEVA**\\ ( value1, value2, ... )                                                                                   |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Estimates standard deviation based on a sample, including numbers, text, and logical values*                         |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **STDEVP**\\ ( number1, number2, ... )                                                                                 |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Calculates standard deviation based on the entire population*                                                        |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **STDEVPA**\\ ( value1, value2, ... )                                                                                  |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Calculates standard deviation based on the entire population, including numbers, text, and logical values*           |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **STEYX**\\ ( known_y's, known_x's )                                                                                   |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the standard error of the predicted y-value for each x in the regression*                                    |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **TDIST**\\ ( x, degrees_freedom, tails )                                                                              |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the Student's t-distribution*                                                                                |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **TINV**\\ ( probability, degrees_freedom )                                                                            |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the inverse of the Student's t-distribution*                                                                 |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **TREND**\\ ( known_y's, known_x's, new_x's, const )                                                                   |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns values along a linear trend*                                                                                 |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **TRIMMEAN**\\ ( array, percent )                                                                                      |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the mean of the interior of a data set*                                                                      |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **TTEST**\\ ( array1, array2, tails, type )                                                                            |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the probability associated with a Student's t-test*                                                          |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **VAR**\\ ( number1, number2, ... )                                                                                    |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Estimates variance based on a sample*                                                                                |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **VARA**\\ ( value1, value2, ... )                                                                                     |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Estimates variance based on a sample, including numbers, text, and logical values*                                   |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **VARP**\\ ( number1, number2, ... )                                                                                   |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Calculates variance based on the entire population*                                                                  |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **VARPA**\\ ( value1, value2, ... )                                                                                    |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Calculates variance based on the entire population, including numbers, text, and logical values*                     |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **WEIBULL**\\ ( x, alpha, beta, cumulative )                                                                           |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the Weibull distribution*                                                                                    |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n|| **ZTEST**\\ (  )                                                                                                       |`Coming soon`_                                         |\r\n|                                                                                                                        |                                                       |\r\n|\\ *Returns the one-tailed probability-value of a z-test*                                                                |                                                       |\r\n+------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------+\r\n\r\nText functions\r\n--------------\r\n\r\n+---------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------+\r\n|Function                                                                                                                         |Python Equivalent                                       |\r\n+=================================================================================================================================+========================================================+\r\n|| **ASC**\\ ( text )                                                                                                              |\\ *Only relevant in VBA*                                |\r\n|                                                                                                                                 |                                                        |\r\n|\\ *Changes full-width (double-byte) English letters or katakana within a character string to half-width (single-byte) characters*|                                                        |\r\n+---------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------+\r\n|| **BAHTTEXT**\\ ( number )                                                                                                       |`Coming soon`_                                          |\r\n|                                                                                                                                 |                                                        |\r\n|\\ *Converts a number to text, using the (baht) currency format*                                                                  |                                                        |\r\n+---------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------+\r\n|| **CHAR**\\ ( number )                                                                                                           |::                                                      |\r\n|                                                                                                                                 |                                                        |\r\n|\\ *Returns the character specified by the code number*                                                                           | chr(i)                                                 |\r\n|                                                                                                                                 |                                                        |\r\n+---------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------+\r\n|| **CLEAN**\\ ( text )                                                                                                            |`Coming soon`_                                          |\r\n|                                                                                                                                 |                                                        |\r\n|\\ *Removes all nonprintable characters from text*                                                                                |                                                        |\r\n+---------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------+\r\n|| **CODE**\\ ( text )                                                                                                             |`Coming soon`_                                          |\r\n|                                                                                                                                 |                                                        |\r\n|\\ *Returns a numeric code for the first character in a text string*                                                              |                                                        |\r\n+---------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------+\r\n|| **CONCATENATE**\\ ( text1, text2, ... )                                                                                         |::                                                      |\r\n|                                                                                                                                 |                                                        |\r\n|\\ *Joins several text items into one text item*                                                                                  | text1 + text2                                          |\r\n|                                                                                                                                 |                                                        |\r\n|                                                                                                                                 |or::                                                    |\r\n|                                                                                                                                 |                                                        |\r\n|                                                                                                                                 | separator = 'X'                                        |\r\n|                                                                                                                                 | separator.join(text1, text2...)                        |\r\n|                                                                                                                                 |                                                        |\r\n|                                                                                                                                 |                                                        |\r\n+---------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------+\r\n|| **DOLLAR**\\ ( number, decimals )                                                                                               |`Coming soon`_                                          |\r\n|                                                                                                                                 |                                                        |\r\n|\\ *Converts a number to text, using the $ (dollar) currency format*                                                              |                                                        |\r\n+---------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------+\r\n|| **EXACT**\\ ( text1, text2 )                                                                                                    |::                                                      |\r\n|                                                                                                                                 |                                                        |\r\n|\\ *Checks to see if two text values are identical*                                                                               | ==                                                     |\r\n|                                                                                                                                 |                                                        |\r\n+---------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------+\r\n|| **FIND,  FINDB**\\ (  )                                                                                                         |::                                                      |\r\n|                                                                                                                                 |                                                        |\r\n|\\ *Finds one text value within another (case-sensitive)*                                                                         | text.find( 'text_to_find' )                            |\r\n|                                                                                                                                 |                                                        |\r\n+---------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------+\r\n|| **FIXED**\\ ( number, decimals, no_commas )                                                                                     |\\ *use string formatting*                               |\r\n|                                                                                                                                 |                                                        |\r\n|\\ *Formats a number as text with a fixed number of decimals*                                                                     |                                                        |\r\n+---------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------+\r\n|| **JIS**\\ ( text )                                                                                                              |`Coming soon`_                                          |\r\n|                                                                                                                                 |                                                        |\r\n|\\ *Changes half-width (single-byte) English letters or katakana within a character string to full-width (double-byte) characters*|                                                        |\r\n+---------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------+\r\n|| **LEFT,  LEFTB**\\ (  )                                                                                                         |::                                                      |\r\n|                                                                                                                                 |                                                        |\r\n|\\ *Returns the leftmost characters from a text value*                                                                            | text[:length]                                          |\r\n|                                                                                                                                 |                                                        |\r\n+---------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------+\r\n|| **LEN,  LENB**\\ (  )                                                                                                           |::                                                      |\r\n|                                                                                                                                 |                                                        |\r\n|\\ *Returns the number of characters in a text string*                                                                            | len(text)                                              |\r\n|                                                                                                                                 |                                                        |\r\n+---------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------+\r\n|| **LOWER**\\ ( text )                                                                                                            |::                                                      |\r\n|                                                                                                                                 |                                                        |\r\n|\\ *Converts text to lowercase*                                                                                                   | text.lower()                                           |\r\n|                                                                                                                                 |                                                        |\r\n+---------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------+\r\n|| **MID,  MIDB**\\ (  )                                                                                                           |::                                                      |\r\n|                                                                                                                                 |                                                        |\r\n|\\ *Returns a specific number of characters from a text string starting at the position you specify*                              | text[start:end]                                        |\r\n|                                                                                                                                 |                                                        |\r\n+---------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------+\r\n|| **PHONETIC**\\ ( reference )                                                                                                    |`Coming soon`_                                          |\r\n|                                                                                                                                 |                                                        |\r\n|\\ *Extracts the phonetic (furigana) characters from a text string*                                                               |                                                        |\r\n+---------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------+\r\n|| **PROPER**\\ ( text )                                                                                                           |::                                                      |\r\n|                                                                                                                                 |                                                        |\r\n|\\ *Capitalizes the first letter in each word of a text value*                                                                    | a_string.title()                                       |\r\n|                                                                                                                                 |                                                        |\r\n+---------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------+\r\n|| **REPLACE,  REPLACEB**\\ (  )                                                                                                   |::                                                      |\r\n|                                                                                                                                 |                                                        |\r\n|\\ *Replaces characters within text*                                                                                              | text.replace( 'str_to_replace', 'str_to_replace_with' )|\r\n|                                                                                                                                 |                                                        |\r\n+---------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------+\r\n|| **REPT**\\ ( text, number_times )                                                                                               |::                                                      |\r\n|                                                                                                                                 |                                                        |\r\n|\\ *Repeats text a given number of times*                                                                                         | text * number_times                                    |\r\n|                                                                                                                                 |                                                        |\r\n+---------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------+\r\n|| **RIGHT,  RIGHTB**\\ (  )                                                                                                       |::                                                      |\r\n|                                                                                                                                 |                                                        |\r\n|\\ *Returns the rightmost characters from a text value*                                                                           | text[-length:]                                         |\r\n|                                                                                                                                 |                                                        |\r\n+---------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------+\r\n|| **SEARCH,  SEARCHB**\\ (  )                                                                                                     |::                                                      |\r\n|                                                                                                                                 |                                                        |\r\n|\\ *Finds one text value within another (not case-sensitive)*                                                                     | text.lower().find( search_string.lower() )             |\r\n|                                                                                                                                 |                                                        |\r\n+---------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------+\r\n|| **SUBSTITUTE**\\ ( text, old_text, new_text, instance_num )                                                                     |::                                                      |\r\n|                                                                                                                                 |                                                        |\r\n|\\ *Substitutes new text for old text in a text string*                                                                           | text.replace( 'str_to_replace', 'str_to_replace_with' )|\r\n|                                                                                                                                 |                                                        |\r\n+---------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------+\r\n|| **T**\\ ( value )                                                                                                               |::                                                      |\r\n|                                                                                                                                 |                                                        |\r\n|\\ *Converts its arguments to text*                                                                                               | str(value)                                             |\r\n|                                                                                                                                 |                                                        |\r\n+---------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------+\r\n|| **TEXT**\\ ( value, format_text )                                                                                               |\\ *use string formatting*                               |\r\n|                                                                                                                                 |                                                        |\r\n|\\ *Formats a number and converts it to text*                                                                                     |                                                        |\r\n+---------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------+\r\n|| **TRIM**\\ ( text )                                                                                                             |::                                                      |\r\n|                                                                                                                                 |                                                        |\r\n|\\ *Removes spaces from text*                                                                                                     | text.strip()                                           |\r\n|                                                                                                                                 |                                                        |\r\n+---------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------+\r\n|| **UPPER**\\ ( text )                                                                                                            |::                                                      |\r\n|                                                                                                                                 |                                                        |\r\n|\\ *Converts text to uppercase*                                                                                                   | text.upper()                                           |\r\n|                                                                                                                                 |                                                        |\r\n+---------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------+\r\n|| **VALUE**\\ ( text )                                                                                                            |::                                                      |\r\n|                                                                                                                                 |                                                        |\r\n|\\ *Converts a text argument to a number*                                                                                         | int(text)                                              |\r\n|                                                                                                                                 |                                                        |\r\n|                                                                                                                                 |or::                                                    |\r\n|                                                                                                                                 |                                                        |\r\n|                                                                                                                                 | float(text)                                            |\r\n|                                                                                                                                 |                                                        |\r\n|                                                                                                                                 |                                                        |\r\n+---------------------------------------------------------------------------------------------------------------------------------+--------------------------------------------------------+\r\n\r\n"
  },
  {
    "path": "documentation/talk.md",
    "content": "# How does a spreadsheet work?\n\n    @hjwp\n\n    www.pythonanywhere.com\n    www.obeythetestinggoat.com\n\n\n\n# this talk\n\nhttps://github.com/pythonanywhere/dirigible-spreadsheet\n\n\"slides\": in repo, documentation/talk.md\n\n\n\n\n\n\ngood colourschemes:  nightsky, pspad, sea\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n# Data structure\n\n```python\nworksheet = {}\nworksheet[row_no, col_no] = cell_object\n```\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n# The most basic calculation... how?\n\n= 1 + 1\n--> should give 2\n* the formula/value distinction\n\n\n```python\nclass Cell:\n    def __init__(self):\n        self.formula = ''\n        self.value = undefined\n\n\ndef calculate(worksheet):\n    for cell in worksheet.values():\n        if cell.formula.startswith('=')\n            cell.value = ???\n        else:\n            cell.value = cell.formula\n```\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n# The most basic calculation - answered\n\n= 1 + 1\n--> should give 2\n* the formula/value distinction\n\n\n```python\nclass Cell:\n    def __init__(self):\n        self.formula = ''\n        self.value = undefined\n\n\ndef calculate(worksheet):\n    for cell in worksheet.values():\n        if cell.formula.startswith('=')\n            cell.value = eval(cell.formula)\n        else:\n            cell.value = cell.formula\n```\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n# Exceptions:\n\n```python\n    try:\n        cell.value = eval(cell.formula)\n    except Exception as e:\n        cell.value = undefined\n        cell.error = str(e)\n    # ...\n```\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n# References\n\n= A1 + A2\nWe want to change this so that A1 and A2 become references to worksheet cells:\n= worksheet[1, 1].value + worksheet[1, 2].value\nie excel formula -> python formula\n\n\n```python\ndef calculate(worksheet):\n        #...\n            cell.value = eval(cell._python_formula)\n        #...\n\n\nclass Cell:\n    def __init__(self):\n        self.value = undefined\n        self._formula = ''\n        self._python_formula = ''\n        self.error = None\n\n    def _set_formula(self, user_input):\n        self._formula = user_input\n        self._python_formula = None\n        if user_input.startswith('='):\n            try:\n                self._python_formula = get_python_formula_from_parse_tree(\n                    parser.parse(user_input)\n                )\n\n            except FormulaError as e:\n                self._python_formula = '_raise(FormulaError(\"{}\"))'.format(e)\n\n    def _get_formula(self):\n        return self._formula\n\n    formula = property(_get_forumla, _set_formula)\n```\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nLet the recursive fun begin!\n\nTest-first:\n\n```python\ndef test_converts_formula_starting_with_equals(self):\n    self.assertEquals(get_python_formula_from_parse_tree(parse('=1')), \"1\")\n    self.assertEquals(get_python_formula_from_parse_tree(parse('=1+2')), \"1+2\")\n\n\ndef test_converts_cell_references_and_adds_space(self):\n    self.assertEquals(\n        get_python_formula_from_parse_tree(parse('=A1')),\n        \"worksheet[(1,1)].value \"\n    )\n\n\ndef test_produces_correct_python(self):\n    self.assertEquals(\n        get_python_formula_from_parse_tree(parse(\n            '=[x * A1 for x in range(5)]'\n        )),\n        '[x * worksheet[(1,1)].value for x in range(5)]'\n    )\n``\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n`\n\nAnd it looks like this:\n\n```python\ndef get_python_formula_from_parse_tree(parse_node):\n    return rewrite(parse_node).flatten()[1:]\n\ndef rewrite(parse_node):\n    if parse_node.type == ParseNode.FL_CELL_RANGE:\n        return rewrite_cell_range(parse_node)\n\n    elif parse_node.type in CELL_REFERENCES:\n        return rewrite_cell_reference(parse_node)\n\n    elif parse_node.type in CONTAIN_COLONS:\n        parse_node.children = map(\n            rewrite,\n            [transform_arrow(child) for child in parse_node.children]\n        )\n\n    else:\n        parse_node.children = map(rewrite, parse_node.children)\n\n    return parse_node\n\ndef rewrite_cell_reference(cell_reference_node):\n    # essentially, transform \"A1\" into \"worksheet[1, 1].value\"\n```\n\n\nOK, but now I can't calculate A3 until I know the values of A2 and A1!\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n# Dependencies\n\n```python\n    def _set_formula(self, value):\n        #...\n        parsed_formula = parser.parse(value)\n\n        self.dependencies = get_dependencies_from_parse_tree(parsed_formula)\n        self._python_formula = get_python_formula_from_parse_tree(parsed_formula)\n```\n\nfind and load constants first...\n\n```python\n\ndef calculate_cell(cell):\n    try:\n        cell.value = eval(cell._python_forumla)\n    #...\n\n\n\ndef load_constants(worksheet):\n    for loc, cell in worksheet.items():\n        if not cell._python_formula:\n            cell.value = cell._formula\n```\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nNow we build a graph and calculate them, starting from \"leaves\"\n\n\n```python\ndef calculate(worksheet):\n    load_constants(worksheet)\n\n    leaves = build_dependency_graph(worksheet)\n\n    while leaves:\n        leaf = leaves.pop()\n        cell = worksheet[leaf.location]\n\n        calculate_cell(cell)\n        \n        remove_from_parents(node, leaves) \n\n\ndef remove_from_parents(node, leaves):\n    for parent in node.parents:\n        parent.children.remove(node)\n        if not parent.children:\n            leaves.append(parent)\n```\n\n(and you can parallelise this stuff too)\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n# Building the dependency graph\n\ntwo-way graph...\n\n```python\ndef build_dependency_graph(worksheet):\n    graph = {}\n    completed = set()\n    for location in worksheet.keys():\n        _generate_cell_subgraph(worksheet, graph, location, completed)\n\n\ndef _generate_cell_subgraph(worksheet, graph, location, completed):\n    if location in completed:\n        return\n\n    cell = worksheet[location]\n    _add_location_dependencies(graph, location, cell.dependencies)\n\n    for dependency_location in cell.dependencies:\n        _generate_cell_subgraph(\n            worksheet, graph, dependency_location, completed \n        )\n\n    completed.add(location)\n\n\ndef _add_location_dependencies(graph, location, dependencies):\n    if location not in graph:\n        graph[location] = Node(location)\n\n    graph[location].children.add(dependencies)\n\n    for dependency in dependencies:\n        if dependency not in graph:\n            graph[dependency] = Node(dependency)\n        graph[dependency].parents.add(location)\n```\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n## Detecting cycles\n\n\n\n```python\ndef build_dependency_graph(worksheet):\n    graph = {}\n    completed = set()\n    for location in worksheet.keys():\n        _generate_cell_subgraph(worksheet, graph, location, completed, path=[])\n\ndef _generate_cell_subgraph(worksheet, graph, location, completed, path):\n    if location in completed:\n        return\n\n    if location in path:\n        raise CycleError(path[path.index(location):] + [location])\n\n    cell = worksheet[location]\n    for dependency_location in cell.dependencies:\n        try:\n            _generate_cell_subgraph(\n                worksheet, graph, dependency_location, completed, path + [location]\n            )\n\n        except CycleError as cycle_error:\n            cell.error = cycle_error\n            if location in cycle_error.path:\n                completed.add(location)\n                raise cycle_error\n\n    _add_location_dependencies(graph, location, cell.dependencies)\n    completed.add(location)\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n```\n\n\n# Custom functions\n\n    def foo(x):\n        return x + 2\n\n\ntime to isolate our evals from the global context a little...\n\n```python\n\ndef calculate(worksheet, usercode):\n    context = {}\n    context['worksheet'] = worksheet\n\n    eval(usercode, context)\n\n    #.... loop thru graph\n        cell.value = eval(cell.python_formula, context)\n```\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n# OK, but what if we want to access to some spreadsheet values?\n\n\n* introducing `load_constants` and `evaluate_formulae`:\n\n```python\n\ndef calculate(worksheet, usercode):\n    load_constants(worksheet)\n\n    context = {}\n    context['worksheet'] = worksheet\n\n    load_constants(worksheet, context)\n\n    eval(usercode, context)\n\n    evaluate_formulae(worksheet, context)\n\n\ndef load_constants(worksheet):\n    for cell in worksheet.values():\n        if not cell.startswith('='):\n            cell.value = cell.formula\n\ndef evaluate_formulae(worksheet, context):\n    leaves = build_dependency_graph(worksheet)\n    while leaves:\n        #...\n        calculate_cell(cell)\n```\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n# OK, but what if the user wants access formula results?\n\n\n\n```python\n\ndef calculate(worksheet, usercode_pre_formula_eval, usercode_post_formula_eval):\n    load_constants(worksheet)\n\n    context = {}\n    context['worksheet'] = worksheet\n\n    load_constants(worksheet, context)\n\n    eval(usercode_pre_formula_eval, context)\n\n    evaluate_formulae(worksheet, context)\n\n    eval(usercode_post_formula_eval, context)\n```\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n# notice we can now programatically generate formulae...\n\n```python\n\nworksheet[1, 3].formula = \"=A1+ A2\"\n```\n\nFirst mwahahaha moment.\n\nBut wait, there's more!\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n# And now, time for some real fun\n\nReminder of what we're doing at the moment...\n\n```python\n\ndef calculate(worksheet, usercode_pre_formula_eval, usercode_post_formula_eval):\n    load_constants(worksheet)\n\n    context = {}\n    context['worksheet'] = worksheet\n\n    load_constants(worksheet, context)\n\n    eval(usercode_pre_formula_eval, context)\n\n    evaluate_formulae(worksheet, context)\n\n    eval(usercode_post_formula_eval, context)\n```\n\n\nThis is what the user sees:\n\n\n```python\nload_constants(worksheet)\n\n# Put code here if it needs to access constants in the spreadsheet\n# and to be accessed by the formulae.  Examples: imports,\n# user-defined functions and classes you want to use in cells.\n\nevaluate_formulae(worksheet)\n\n# Put code here if it needs to access the results of the formulae.\n```\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nthe usercode *is* the spreadsheet!\n\n```python\n\ndef load_constants(worksheet):\n    #...\n\ndef evaluate_formulae(worksheet, context):\n    #...\n\ndef calculate(worksheet, usercode, private_key):\n    evaluate_formulae_in_context = lambda worksheet: \\\n        evaluate_formulae(worksheet, context)\n    context = {\n        'worksheet': worksheet,\n        'load_constants': load_constants,\n        'evaluate_formulae': evaluate_formulae_in_context,\n        'undefined': undefined,\n    }\n    try:\n        exec(usercode, context)\n    except Exception as e:\n        add_to_console(e)\n```\n\n\n\n\nso an example, a retirement calculator\n\n"
  },
  {
    "path": "documentation/talk_example_sheets.json",
    "content": "[\n{\n  \"fields\": {\n    \"column_widths_json\": \"{}\",\n    \"api_key\": \"81776bcb-700f-46fa-ba49-cea77b5a84f2\",\n    \"version\": 148,\n    \"usercode\": \"worksheet[\\\"A4\\\"].formula = \\\"=A1+A3\\\"\\n\\ndef foo(something):\\n    return something * 3\\n\\nload_constants(worksheet)\\n\\ndef add_a1(something):\\n    a = worksheet[\\\"A1\\\"]\\n    return something + a1.value\\n\\nevaluate_formulae(worksheet)\\n\\nprint sum(\\n    worksheet[1, r].value\\n    for r in range(1,5)\\n)\\n\\n\\n\\n\\n\\n\\n\\n\",\n    \"contents_json\": \"{ \\\"_console_text\\\": \\\"<span class=\\\\\\\"console_output_text\\\\\\\">13</span><span class=\\\\\\\"console_output_text\\\\\\\">\\\\n</span><span class=\\\\\\\"console_system_text\\\\\\\">Took 0.01s</span>\\\", \\\"_usercode_error\\\": null ,\\\"1,2\\\": { \\\"formula\\\": \\\"=foo(A1)\\\", \\\"formatted_value\\\": \\\"3\\\" , \\\"python_formula\\\": \\\"foo(worksheet[(1,1)].value )\\\" , \\\"dependencies\\\": [[1, 1]] , \\\"value\\\": 3 },\\\"1,3\\\": { \\\"formula\\\": \\\"=A1 + A2\\\", \\\"formatted_value\\\": \\\"4\\\" , \\\"python_formula\\\": \\\"worksheet[(1,1)].value + worksheet[(1,2)].value \\\" , \\\"dependencies\\\": [[1, 1], [1, 2]] , \\\"value\\\": 4 },\\\"1,1\\\": { \\\"formula\\\": \\\"1\\\", \\\"formatted_value\\\": \\\"1\\\" , \\\"value\\\": 1 },\\\"1,4\\\": { \\\"formula\\\": \\\"=A1+A3\\\", \\\"formatted_value\\\": \\\"5\\\" , \\\"python_formula\\\": \\\"worksheet[(1,1)].value +worksheet[(1,3)].value \\\" , \\\"dependencies\\\": [[1, 1], [1, 3]] , \\\"value\\\": 5 } }\",\n    \"timeout_seconds\": 55,\n    \"height\": 1000,\n    \"width\": 52,\n    \"last_modified\": \"2014-09-19T09:19:57.211\",\n    \"owner\": 1,\n    \"is_public\": false,\n    \"allow_json_api_access\": false,\n    \"name\": \"complete usercode\"\n  },\n  \"model\": \"sheet.sheet\",\n  \"pk\": 1\n},\n{\n  \"fields\": {\n    \"column_widths_json\": \"{}\",\n    \"api_key\": \"43043429-d103-4823-bc7c-c66f38c5429e\",\n    \"version\": 0,\n    \"usercode\": \"\\nload_constants(worksheet)\\n\\n# Put code here if it needs to access constants in the spreadsheet\\n# and to be accessed by the formulae.  Examples: imports,\\n# user-defined functions and classes you want to use in cells.\\n\\nevaluate_formulae(worksheet)\\n\\n# Put code here if it needs to access the results of the formulae.\\n\",\n    \"contents_json\": \"{ \\\"_console_text\\\": \\\"\\\", \\\"_usercode_error\\\": null  }\",\n    \"timeout_seconds\": 55,\n    \"height\": 1000,\n    \"width\": 52,\n    \"last_modified\": \"2014-09-19T09:19:40.446\",\n    \"owner\": 1,\n    \"is_public\": false,\n    \"allow_json_api_access\": false,\n    \"name\": \"usercode welcome\"\n  },\n  \"model\": \"sheet.sheet\",\n  \"pk\": 2\n},\n{\n  \"fields\": {\n    \"column_widths_json\": \"{}\",\n    \"api_key\": \"40738830-f386-4fac-b3cf-13ca63fb6cf6\",\n    \"version\": 8,\n    \"usercode\": \"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\nload_constants(worksheet)\\n\\n# Put code here if it needs to access constants in the spreadsheet\\n# and to be accessed by the formulae.  Examples: imports,\\n# user-defined functions and classes you want to use in cells.\\n\\nevaluate_formulae(worksheet)\\n\\n# Put code here if it needs to access the results of the formulae.\\n\",\n    \"contents_json\": \"{ \\\"_console_text\\\": \\\"<span class=\\\\\\\"console_system_text\\\\\\\">Took 0.00s</span>\\\", \\\"_usercode_error\\\": null  }\",\n    \"timeout_seconds\": 55,\n    \"height\": 1000,\n    \"width\": 52,\n    \"last_modified\": \"2014-09-19T09:20:24.857\",\n    \"owner\": 1,\n    \"is_public\": false,\n    \"allow_json_api_access\": false,\n    \"name\": \"custom function\"\n  },\n  \"model\": \"sheet.sheet\",\n  \"pk\": 3\n},\n{\n  \"fields\": {\n    \"column_widths_json\": \"{}\",\n    \"api_key\": \"d4ed8e02-0356-4288-a223-c2b5696f68b8\",\n    \"version\": 14,\n    \"usercode\": \"if True:\\n                                            load_constants(worksheet)\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\ndef sum_as():\\n    pass\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\nif True:\\n                                            evaluate_formulae(worksheet)\\n\",\n    \"contents_json\": \"{ \\\"_console_text\\\": \\\"<span class=\\\\\\\"console_system_text\\\\\\\">Took 0.00s</span>\\\", \\\"_usercode_error\\\": null ,\\\"1,2\\\": { \\\"formula\\\": \\\"2\\\", \\\"formatted_value\\\": \\\"2\\\" , \\\"value\\\": 2 },\\\"1,3\\\": { \\\"formula\\\": \\\"=sum_as()\\\", \\\"formatted_value\\\": \\\"None\\\" , \\\"python_formula\\\": \\\"sum_as()\\\" , \\\"value\\\": null },\\\"1,1\\\": { \\\"formula\\\": \\\"1\\\", \\\"formatted_value\\\": \\\"1\\\" , \\\"value\\\": 1 } }\",\n    \"timeout_seconds\": 55,\n    \"height\": 1000,\n    \"width\": 52,\n    \"last_modified\": \"2014-09-19T09:21:32.516\",\n    \"owner\": 1,\n    \"is_public\": false,\n    \"allow_json_api_access\": false,\n    \"name\": \"usercode accesses values\"\n  },\n  \"model\": \"sheet.sheet\",\n  \"pk\": 4\n},\n{\n  \"fields\": {\n    \"column_widths_json\": \"{}\",\n    \"api_key\": \"9d30f966-14c7-4b8a-b259-13672dbc503d\",\n    \"version\": 12,\n    \"usercode\": \"if True:\\n                                        load_constants(worksheet)\\nif True:\\n                                        evaluate_formulae(worksheet)\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\nprint(sum([]\\n))\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\",\n    \"contents_json\": \"{ \\\"_console_text\\\": \\\"<span class=\\\\\\\"console_output_text\\\\\\\">0</span><span class=\\\\\\\"console_output_text\\\\\\\">\\\\n</span><span class=\\\\\\\"console_system_text\\\\\\\">Took 0.01s</span>\\\", \\\"_usercode_error\\\": null ,\\\"1,2\\\": { \\\"formula\\\": \\\"2\\\", \\\"formatted_value\\\": \\\"2\\\" , \\\"value\\\": 2 },\\\"1,3\\\": { \\\"formula\\\": \\\"=A1 + A2\\\", \\\"formatted_value\\\": \\\"3\\\" , \\\"python_formula\\\": \\\"worksheet[(1,1)].value + worksheet[(1,2)].value \\\" , \\\"dependencies\\\": [[1, 1], [1, 2]] , \\\"value\\\": 3 },\\\"1,1\\\": { \\\"formula\\\": \\\"1\\\", \\\"formatted_value\\\": \\\"1\\\" , \\\"value\\\": 1 } }\",\n    \"timeout_seconds\": 55,\n    \"height\": 1000,\n    \"width\": 52,\n    \"last_modified\": \"2014-09-19T09:24:55.911\",\n    \"owner\": 1,\n    \"is_public\": false,\n    \"allow_json_api_access\": false,\n    \"name\": \"usercode access formula results\"\n  },\n  \"model\": \"sheet.sheet\",\n  \"pk\": 5\n},\n{\n  \"fields\": {\n    \"column_widths_json\": \"{}\",\n    \"api_key\": \"35dd4be2-f8dd-4113-9c26-0149418a5b6d\",\n    \"version\": 4,\n    \"usercode\": \"\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\nif True:\\n                                        load_constants(worksheet)\\n                                        \\nif True:\\n                                        evaluate_formulae(worksheet)\\n\\n\",\n    \"contents_json\": \"{ \\\"_console_text\\\": \\\"<span class=\\\\\\\"console_system_text\\\\\\\">Took 0.00s</span>\\\", \\\"_usercode_error\\\": null  }\",\n    \"timeout_seconds\": 55,\n    \"height\": 1000,\n    \"width\": 52,\n    \"last_modified\": \"2014-09-19T09:27:51.238\",\n    \"owner\": 1,\n    \"is_public\": false,\n    \"allow_json_api_access\": false,\n    \"name\": \"Basic demo sheet\"\n  },\n  \"model\": \"sheet.sheet\",\n  \"pk\": 6\n}\n]\n"
  },
  {
    "path": "documentation/tutorial01.rst",
    "content": "Tutorial part 1: First steps, adding Python to a spreadsheet\r\n============================================================\r\n\r\nThis tutorial aims to get you started with Dirigible as quickly as possible.\r\n\r\nIt is made up of a number of examples of increasing complexity, each of which\r\nshows one or more useful Dirigible features -- this page contains the first.\r\n\r\n\r\nWriting your first Dirigible spreadsheet\r\n----------------------------------------\r\n\r\nThe first spreadsheet we'll build using Dirigible is a very simple price list,\r\nwith a few useful functions.  Our aim is just to give you a taste of how\r\nDirigible differs from traditional spreadsheet programming.  We'll move on to\r\nmore interesting examples in a later tutorial.\r\n\r\nWe'll also assume that you have at least a basic knowledge of programming; if\r\nyou can code Python, C#, Java, Perl, C, C++, or something similar reasonably\r\nwell, you should be fine.\r\n\r\nWe strongly recommend that you create your own sheet by working through this\r\ntutorial.\r\n\r\n\r\nCreating the sheet\r\n^^^^^^^^^^^^^^^^^^\r\n\r\nThe first page that you see when you log in to Dirigible is your *dashboard*, a\r\npage that summarises all of the details of your Dirigible account.  It will\r\nlook something like this:\r\n\r\n.. image:: tutorial-01-dashboard.png\r\n\r\nHere you can see a list of all of your spreadsheets; if you've only just signed\r\nup, the list will be pretty short!  You can always access your dashboard from\r\nanywhere within Dirigible by using the \"My account\" link at the top right of\r\nthe page.\r\n\r\nTo get started with this tutorial, click the \"Create new sheet\" link at the top\r\nright of the list of spreadsheets.  You'll be taken to a new page, showing an\r\nempty spreadsheet:\r\n\r\n.. image:: tutorial-01-sheet-page.png\r\n\r\nWe'll call this the *sheet page*.  Its layout should be pretty\r\nself-explanatory; there's some navigational stuff at the top, the name of the\r\nsheet (which will initially be something like \"Sheet 111\") , and then a\r\nspreadsheet grid on the left-hand side and a code editor on the right.  The\r\nspreadsheet grid is where you enter data and formulae, just as you would in any\r\nspreadsheet.  The code editor is where you enter your own Python code to\r\naugment and control the recalculation of the spreadsheet; we call this\r\n*usercode*, and you'll find out more about it later.  Underneath the usercode\r\neditor is the output window; more about that later too.\r\n\r\n\r\nSome basic setup\r\n^^^^^^^^^^^^^^^^\r\n\r\nBefore we enter any data into the sheet, let's customize it a little.  If you\r\nmove the mouse over the sheet name then you'll see its background go grey.\r\nClick it, and you will be able to edit it.  Type ``Price list`` and hit return\r\nto change it.\r\n\r\nNext, let's edit the usercode a little.  Initially, this has calls to two\r\nfunctions, :func:`load_constants` and :func:`evaluate_formulae`.\r\nThere'll be more about these later, but for now, all you need to know is that\r\nthey are important, and you shouldn't delete them!\r\n\r\nThere are also some comments -- lines starting with ``#``.  For now, we'll just\r\nadd a new comment to the top.  Click just before the call to\r\n:func:`load_constants`, add a few blank lines, and then enter the following:\r\n\r\n::\r\n\r\n    # Tutorial, example 1\r\n    # Price list\r\n    # Written by YOUR NAME on DATE.\r\n\r\nNow, click back in the spreadsheet grid.  Because the only change you've made\r\nis adding a few comments, nothing else should have changed.\r\n\r\n.. image:: tutorial-01-after-basic-setup.png\r\n\r\nLet's add some data to the table.\r\n\r\nEntering data\r\n^^^^^^^^^^^^^\r\n\r\nClick on cell A1 in the grid, and type ``Product``.  Hit the tab key to move on\r\nto cell B1, and type ``Net price`` -- note that while you type, the text\r\nappears both in the cell and in the formula bar above the grid, just like in\r\nany other spreadsheet.  Tab again to C1, and enter ``Gross price``, then hit\r\nreturn.  Use the cursor keys or the mouse to move to cell A2, and type the\r\nfollowing products for our price list (hitting enter after each):\r\n\r\n* Paper\r\n* Printer ink\r\n* Printer cables\r\n* AA batteries\r\n* Pens\r\n* Notepads\r\n\r\nOnce you've done this, adjust the widths of columns A and C so that everything\r\nfits; if you move the mouse pointer over the header between columns A and B,\r\nyou should see it turn into a double-headed arrow; click and drag at that point\r\nto adjust column A's width, and then do the same for column C so that all of\r\nthe text \"Gross price\" is visible.  Once that's done, you should have a\r\nspreadsheet that looks something like this:\r\n\r\n.. image:: tutorial-01-products.png\r\n\r\nFinally, put some net prices (that is, the price of each product before sales\r\ntax) into column B:\r\n\r\n.. image:: tutorial-01-net-prices.png\r\n\r\nAdding a formula\r\n^^^^^^^^^^^^^^^^\r\n\r\nNow that we've got some data in the grid, let's add a formula.  Click on cell\r\nC2, and enter our first attempt at a calculation to add the tax to the gross\r\nprice.  We'll use a tax rate of 17.5%, so the formula needs to be ``=B2 *\r\n1.175``; type that in, and hit return.\r\n\r\n.. image:: tutorial-01-gross-price-1.png\r\n\r\nNow, this worked, but it's started to make the spreadsheet a bit opaque.  What\r\nwould happen if the tax rate changed?  Finding which formulae across many cells\r\nreferenced the value 1.175 would be error-prone, especially if the spreadsheet\r\ngot bigger.  So let's change it and make sure we have a tax rate in just one\r\nplace.\r\n\r\n\r\nUsing usercode variables in formulae\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nEdit the usercode, and just underneath where you put your comment earlier, add\r\nthis code:\r\n\r\n::\r\n\r\n    # Conversion factor for current tax rate\r\n    tax_factor = 1.175\r\n\r\nNow, double-click cell C2 and edit the formula so that it reads ``=B2 *\r\ntax_factor``.  Hit return; the results should be unchanged, but now you can\r\nchange the tax by adjusting the user code.  Try changing ``tax_factor`` to,\r\nsay, 1.2 in the usercode, then save the change (by clicking on the grid or\r\npressing control-S) and you'll see the gross price in C2 change; change it back\r\nto 1.175 and see the gross price change back.\r\n\r\nBut what if the tax situation were more complex?  UK sales tax, for example,\r\nisn't charged on certain kinds of goods.  In situations like that, a simple\r\n``tax_factor`` value isn't enough -- we need a function.\r\n\r\nUsing usercode functions in formulae\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nDescribing the details of any tax system in this tutorial would be boring and\r\nunnecessary, so let's write a function to handle an imaginary country where\r\npens and paper are taxed at 5% and everything else is taxed at 15%.  Replace\r\nthe tax factor usercode with this function definition:\r\n\r\n::\r\n\r\n    stationery_tax_factor = 1.05\r\n    other_tax_factor = 1.15\r\n    def add_tax(product, price):\r\n        if product.lower() in (\"pens\", \"paper\"):\r\n            return price * stationery_tax_factor\r\n        return price * other_tax_factor\r\n\r\nAs soon as you save the usercode, you'll see an error message appear in cell\r\nC2.  If you move the mouse pointer over it, you should see a popup explaining\r\nthe problem:\r\n\r\n.. image:: tutorial-01-error-in-grid.png\r\n\r\nIf you look down at the output console, in the bottom right, underneath the\r\nusercode editor, you will see that the error is displayed there too:\r\n\r\n.. image:: tutorial-01-error-in-output-console.png\r\n\r\nAll errors are shown in the output console, which can be very useful as your\r\nspreadsheets get larger and cells containing errors are no longer necessarily\r\nvisible.\r\n\r\nAnyway, the problem that is causing these errors is clear enough; our formula\r\nstill refers to the variable ``tax_factor``, which we have replaced with our\r\nnew function.  To fix the problem, double-click on C2 to edit its formula, and\r\nchange it to ``=add_tax(A2, B2)``.  The errors will disappear, and you'll see\r\nthe new value with the 5% stationery tax rate:\r\n\r\n.. image:: tutorial-01-gross-price-2.png\r\n\r\nNext, enter the equivalent formula into C3: ``=add_tax(A3, B3)``, and you'll\r\nsee that the printer ink gets the higher 15% tax:\r\n\r\n.. image:: tutorial-01-gross-price-3.png\r\n\r\nNow, we could enter similar formulae for every other cell in column C, but this\r\nwould be tedious.  We could copy the formula down, but then we'd be duplicating\r\ncode, so if we changed the formula later, we'd need to make the same change\r\nmultiple times.  As every programmer knows, when you do the same thing multiple\r\ntimes, it's better to use a loop than to copy and paste things.\r\n\r\n.. _generating-formulae:\r\n\r\nGenerating formulae from usercode\r\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n\r\nJust underneath the usercode you've added but *before* the\r\n``evaluate_formulae`` line, add the following:\r\n\r\n::\r\n\r\n    row = 2\r\n    while (worksheet['A', row].formula and worksheet['B', row].formula):\r\n        worksheet['C', row].formula = '=add_tax(A%d, B%d)' % (row, row)\r\n        row += 1\r\n\r\nThis simple Python loop runs through the worksheet from row 2 downwards until\r\nit encounters a blank row, and for each one it puts a formula like the one we\r\nwrote earlier into column C.  Click back in the grid or hit control-S to save\r\nthe usercode change, and you'll see gross prices filled in for all of the\r\nproducts.  Click on one of the cells in column C, and you'll see that the\r\nformula there has the correct row numbers in it:\r\n\r\n.. image:: tutorial-01-gross-price-4.png\r\n\r\nNow, add a new row to the bottom of the list; put ``Staples`` into A8 and\r\n``0.99`` into B8.  As soon as you hit return in cell B8, you'll see that the\r\ngross price automatically gets its formula and the resulting value.\r\n\r\n.. image:: tutorial-01-gross-price-5.png\r\n\r\nAnd that's it for now!\r\n\r\nIn conclusion\r\n^^^^^^^^^^^^^\r\n\r\nIn this part of the tutorial, we've shown how to use Dirigible like a normal\r\nspreadsheet, and then extended that to show how you can interact with the\r\nspreadsheet grid from usercode to make a more resilient, more extendable\r\ncalculation model.\r\n\r\nIn :doc:`the next part of the tutorial <tutorial02>`, we'll show how you can\r\nperform much more advanced goal-seeking calculations by changing the simple\r\ncall to the :func:`evaluate_formulae` function that Dirigible gives you by\r\ndefault.\r\n"
  },
  {
    "path": "documentation/tutorial02.rst",
    "content": "Tutorial part 2: Getting more control of your spreadsheet with usercode\n=======================================================================\n\nThis section of the tutorial explains more about the usercode - the area\nof source code to the right of the grid. We'll show how to decide whereabouts\nin the usercode you should make your own changes, and how you can restructure\nthe usercode to perform tasks like goal-seeking.\n\nWe strongly recommend that you create your own sheet by working through\nthis tutorial.\n\n\nThe anatomy of usercode\n-----------------------\n\nWhen you first create a sheet, it contains the following usercode::\n\n    load_constants(worksheet)\n\n    # Put code here if it needs to access constants in the spreadsheet\n    # and to be accessed by cell formulae.  Examples: imports,\n    # user-defined functions and classes you want to use in cells.\n\n    evaluate_formulae(worksheet)\n\n    # Put code here if it needs to access the results of cell formulae.\n\nThis code is written in `Python <http://python.org>`_, and it gets executed\nevery time you make a change to your sheet. In order to understand its\noperation, we need to quickly revisit the humble spreadsheet cell.\n\nA cell has two attributes: its *formula*, which is what gets set when you type\nsomething into the cell. Also, it has a *value*, which is what is displayed\non-screen in the grid.\n\nThe function call :func:`load_constants` populates the displayed values in\nevery cell containing a *constant* - that is, a formula which consists of\na number or text which isn't prefixed by an '=' character.\n\nThe second function call, :func:`evaluate_formulae`, populates all the\nremaining cells - the ones containing a formula that begins with '='. It\ndoes this by *evaluating* the formula, and putting the result into the cell's\nvalue.\n\nAs we'll see, this has implications for whereabouts in the usercode we should\nplace the following types of changes.\n\n\nDefining values for use in cell formulae\n----------------------------------------\n\nAs you saw in the previous tutorial, usercode can define values or functions\nfor use in cell formulae::\n\n    load_constants(worksheet)\n\n    interest_rate = 1.175\n\n    def is_odd(n):\n        if n % 2 == 0:\n            return False\n        else:\n            return True\n\n    evaluate_formulae(worksheet)\n\nImportantly, notice how both the value *interest_rate* and the function\n*is_odd* are defined before the call to :func:`evaluate_formulae`. As a\nconsequence, we can use *interest_rate* in cell formulae:\n\n.. image:: tutorial-02-using-variables-in-cell-formulae.png\n\nand we can use the function *is_odd* in cell formulae:\n\n.. image:: tutorial-02-defining-functions.png\n\nHowever, if we move the definition of either to a point after the call to\n:func:`evaluate_formulae()`::\n\n    load_constants(worksheet)\n\n    evaluate_formulae(worksheet)\n\n    interest_rate = 1.175\n\nthen trying to use *interest_rate* in cell formulae will now cause an error:\n\n.. image:: tutorial-02-using-variables-in-cell-formulae-error.png\n\n.. image:: tutorial-02-using-variables-in-cell-formulae-error-message.png\n\nThis is because while the formula in cell A1 is being evaluated (inside the\ncall to :func:`evaluate_formulae`), *interest_rate* has not yet been\ndefined.\n\nSimilarly, symbols and functions can be imported from the `Python standard\nlibrary <http://docs.python.org/library/index.html>`_. If you intend to use\nthem in your cell formulae though, the import must be placed before the call\nthe :func:`evaluate_formulae`. For example::\n\n    load_constants(worksheet)\n\n    from math import cos, pi\n\n    evaluate_formulae(worksheet)\n\nThe value *pi* and the function *cos* can now be used in cell formulae.\n\n\nUsing cell values in usercode\n-----------------------------\n\nPreviously we have seen that objects must be defined in usercode before\nthe call to :func:`evaluate_formulae` if we are to use them in cell formulae.\n\nConversely, sometimes we want to write usercode which uses values from cells.\nIn this case, we must be sure to position such usercode *after* the call to\n:func:`evaluate_formulae`.\n\nImagine our usercode contained a formula that should be executed several\ntimes, and cell A1 contained the number of times it should repeat.\n\n**Put the value 4 into cell A1**, then add the following usercode::\n\n    z = 0\n    for i in range(worksheet.A1.value):\n        z = z * z + 2\n    print z\n\n    load_constants(worksheet)\n\n    evaluate_formulae(worksheet)\n\nThis will cause an error in the usercode:\n\n.. image:: tutorial-02-using-cell-values-in-usercode-error.png\n\nWorksheet.A1.value is Undefined, because when we try to access it,\nwe haven't yet called either of the functions that\npopulate cell values.\n\nLets move the for-loop to after :func:`load_constants`::\n\n    load_constants(worksheet)\n\n    z = 0\n    for i in range(worksheet.A1.value):\n        z = z * z + 2\n    print z\n\n    evaluate_formulae(worksheet)\n\nClick away from the usercode editor (e.g. on a cell) to save these changes\nand recalculate the sheet.\n\nThis will now print the correct result '1446' in the output pane, on the bottom\nright. This is because the constant value of '4' in cell A1 is populated by the\ncall to :func:`load_constants`. Then, after that is done, we can use the value\nof cell A1 in our usercode.\n\nLet's try something else. **Change the value in A1 from '4' to '=4'** (without quotes).\n\nNow we get the error in our usercode again. This happens because our usercode\nis trying to use the value in cell A1, but this cell's value is no longer set\nby :func:`load_constants`. Because A1 now contains a formula instead of a\nconstant, its value is now set by :func:`evaluate_formulae`.\n\nIn order to make the usercode work, we have to move the for-loop to after\n:func:`evaluate_formulae`::\n\n    load_constants(worksheet)\n\n    evaluate_formulae(worksheet)\n\n    z = 0\n    for i in range(worksheet.A1.value):\n        z = z * z + 2\n    print z\n\nThis produces the correct result '1446' in the output pane again.\n\n\nGoal Seeking\n------------\n\nHere's a more advanced example. To demonstrate goal-seeking, we're going\nto create a simple retirement planning spreadsheet which calls\n:func:`evaluate_formula` several times, tweaking the spreadsheet\ninput cell values each time, until it finds a desireable outcome.\n\nLet's start off by providing our spreadsheet with some inputs. Enter\nthe following values:\n\n==== ====================================== =======\n \\     A                                      B\n==== ====================================== =======\n  1   INPUTS:\n---- -------------------------------------- -------\n  2   Current age                              28\n---- -------------------------------------- -------\n  3   Desired retirement age                   50\n---- -------------------------------------- -------\n  4   Max monthly savings                    1500\n---- -------------------------------------- -------\n  5   Expected monthly retirement expenses   2500\n---- -------------------------------------- -------\n  6\n---- -------------------------------------- -------\n  7   Expected inflation                     0.065\n---- -------------------------------------- -------\n  8   Expected returns                       0.075\n==== ====================================== =======\n\nWe're going define a function in the usercode, that will be used from cell\nformula. Add this right before the call to :func:`evaluate_formulae`::\n\n    # A simple FV calculator\n    def future_value(interest_rate, number_payments, payment_amount):\n        return (\n            payment_amount *\n            ((interest_rate + 1) ** number_payments - 1) /\n            interest_rate\n        )\n\n\nNext up, let's label some cells for the spreadsheet outputs:\n\n==== ===================================== ==========================================\n \\     A                                     B\n==== ===================================== ==========================================\n 14   OUTPUTS:\n---- ------------------------------------- ------------------------------------------\n 15   How much you need to save per month\n---- ------------------------------------- ------------------------------------------\n 16   Cash generated by these savings       =future_value(B8/12, (B17-B2) * 12, B15)\n---- ------------------------------------- ------------------------------------------\n 17   Actual retirement age\n==== ===================================== ==========================================\n\nThese two empty outputs, amount to save and actual retirement age, will be\nused in the remainder of our calculations, so for now lets add some usercode\nto populate them with reasonable initial values. Add this right after\nthe *future_value* function::\n\n    # default values for amount to save\n    worksheet.b15.value = worksheet.b4.value\n    # default value for actual retirement age\n    worksheet.b17.value = worksheet.b3.value\n\nThis will populate cells B15 and B17.\n\nNow lets add more cell formula inbetween the inputs and the outputs, to\ncalculate some intermediate values:\n\n==== ==================================== ==================================\n \\     A                                    B\n==== ==================================== ==================================\n 10   WORKING:\n---- ------------------------------------ ----------------------------------\n 11   Yearly expenses at retirement        =B5 * (1 + B7) ** (B17 - B2) * 12\n---- ------------------------------------ ----------------------------------\n 12   Amount required for annuity          =B11 / B8\n==== ==================================== ==================================\n\nCell B11 calculates our inflation-adjusted annual expenditure at\nretirement, using the 'actual retirement age' we just defined in B17.\nCell B12 shows the size of investment we'll need in order to generate an\nannuity that large.\n\nThese calculations expose a problem with our plan. The amount required\nto generate our desired annuity (B12) comes out to around 1,598,000. However,\nthe estimated value of our savings at that time (B16) is only 1,003,000.\nSomething is going to have to give.\n\nLet's write some usercode to find an optimal solution.\n\n\nIncreasing the retirement age\n.............................\n\nLet's **replace** the call to :func:`evaluate_formulae` with something a\nlittle smarter::\n\n    def have_enough():\n        return worksheet.b16.value >= worksheet.b12.value\n\n    # populate output cells with initial values\n    evaluate_formulae(worksheet)\n\n    # If we don't have enough then we're going to have to increase\n    # the retirement age and try again\n    while not have_enough():\n        worksheet.b17.value += 1\n        evaluate_formulae(worksheet)\n\nClick away from the usercode editor (e.g. on a cell) to save these changes\nand recalculate the sheet.\n\nHere we've defined a function *have_enough*, that returns true if our\nsavings at retirement equal or exceed the amount we'll need to generate\nour desired annuity.\n\nThen we call :func:`evaluate_formulae` once, to seed the output cells with\ninitial values.\n\nFinally, we use we a Python *while-loop* to repeatedly evaluate the cell\nformula until *have_enough* returns True, increasing the actual retirement\nage by one year every time.\n\nThis reveals, by the resulting value in cell B17, that the current plan will\nrequire us to wait until age 75 before we can retire on the income we\ndesire.\n\nLet's adjust our expectations then, to prepare for a more frugal future.\n**Decrease the expected monthly retirement expenses** in cell B5, to 1400.\nThe actual retirement age (B17) now goes down to 50 - exactly as we desired\n(B3).\n\n\nDecreasing the amount saved\n...........................\n\nOur investment at retirement (B16=1,003,000) is now substantially larger than\nthe amount we need to generate our annuity (B12=895,000.) So we're saving more\nthan we need to. Can we find out how much we could reduce our monthly savings\nby, and still make our retirement goal?\n\nAdd the following usercode, between the single call to\n:func:`evaluate_formulae` and the final while loop::\n\n    # Use a binary chop to find the amount to save per month\n    # if it's less than the maximum\n    def bin_chop(minimum, maximum):\n        worksheet.b15.value = minimum\n        evaluate_formulae(worksheet)\n        if have_enough():\n            return\n\n        worksheet.b15.value = maximum\n        evaluate_formulae(worksheet)\n        if not have_enough():\n            return\n\n        # It's somewhere between the min and the max\n        if (maximum - minimum) <= 1:\n            # Close enough, let's bail out.\n            return\n\n        half = (maximum - minimum) / 2\n        bin_chop(minimum, minimum + half)\n        if have_enough():\n            return\n\n        bin_chop(minimum + half, maximum)\n    bin_chop(0, worksheet.b4.value)\n\nClick away from the usercode editor (e.g. on a cell) to save these changes\nand recalculate the sheet.\n\nThis code defines a function, *bin_chop*, that is given a minimum and maximum\namount to save. It uses a binary search to find the minimum amount\nneeded to save until we *have_enough*. We then call this function in the\nfinal line of the above snippet, passing a minimum of 0 and a maximum of\nthe user's desired maximum savings from B4. This reveals that to meet our\nretirement goals, the amount we need to save, in B15, need be only 1339.\n\n\nIn conclusion\n-------------\n\nIn this part of the tutorial, we've shown that the usercode is executed to\ngenerate the results displayed in your spreadsheet. The functions it calls,\n:func:`load_constants` and :func:`evaluate_formulae`, populate the values of\ncells based upon the cell's values. We've examined the implications this has\nfor whereabouts in the usercode you should place your own custom usercode. This\ndepends on whether it needs access to cell values, or provides values and\nsymbols for cells. Finally, we showed a more advanced example of usercode,\nwhich calls :func:`evaluate_formulae` several times until a goal is found.\n\nIn :doc:`the next part of the tutorial <tutorial03>`, we'll show how you can\nuse the Python `NumPy <http://numpy.scipy.org/>`_ library along with\nDirigible's ability to store Python objects in the spreadsheet grid, to build\nsimple spreadsheets that process large amounts of data without copying and\npasting formulae thousands of times.\n\n"
  },
  {
    "path": "documentation/tutorial03.rst",
    "content": "Tutorial part 3: Dealing with lots of data, using NumPy and objects in the grid\n===============================================================================\n\nOne of Dirigible's greatest strengths is that cells in the spreadsheet grid aren't limited to holding numbers or text; each and every cell can hold any kind of Python object.  What this means is that one cell can contain an essentially unlimited amount of data, which can then be processed in bulk by your formulae -- meaning less duplication and easier maintenance.\n\nThis part of the tutorial introduces you to the Python `NumPy <http://numpy.scipy.org/>`_ library, a great source of numerical and statistical functions, and shows how you can build a useful spreadsheet where certain cells hold large arrays of numbers.\n\nWe strongly recommend that you create your own sheet by working through this tutorial.\n\nLoading some basic data\n^^^^^^^^^^^^^^^^^^^^^^^\n\nThe spreadsheet we're going to build will take some basic input values that describe the orbit of `the planet Mercury <http://en.wikipedia.org/wiki/Mercury_%28planet%29>`_.  It will use these to calculate a set of four 700-element arrays that give *x*, *y*, *z* and time coordinates that describe a single orbit of the planet around the Sun.  We won't go into the details of orbital mechanics, but will just give the formulae that you'll need to enter -- if you're keen on finding out more about the underlying mathematics, we recommend this page on `How to Calculate the Positions of the Planets <http://www.davidcolarusso.com/astro/>`_ and, if you're using a WebGL-capable browser like Chrome 9 or Firefox 4.0, `this WebGL tool <http://dl.dropbox.com/u/13551192/astrodynamics101/orbit.html>`_.\n\nThe very first step is to get some basic data into the spreadsheet.  We need values for the orbit's eccentricity (how close it is to being a perfect circle), the orbital period (the number of days for one complete orbit), the semimajor axis (a measure of the orbit's diameter), the inclination to the Sun's equator (the tilt of the orbit), and two terms that describe how the orbit is oriented with respect to the Sun's equator, known as the longitude of the ascending note and the argument of perihelion.  To save the time it would take to type these in manually, you can `download a comma-separated value file from here <https://s3.amazonaws.com/planet-data/mercury.csv>`_.  Once you've downloaded the the file to your machine, create a new sheet in Dirigible and click the import CSV button to import it:\n\n.. image:: tutorial-03-csv-import-button.png\n\nUpload the new file, adjust the width of column A so that you can read everything, and give the sheet a name -- you'll end up with something like this:\n\n.. image:: tutorial-03-after-csv-import.png\n\nThe next step is to get started with some formulae.\n\nSimple formulae\n^^^^^^^^^^^^^^^\n\nBefore we create the powerful NumPy formulae that work with arrays of data, we'll need to create a few simple ones and a bit of usercode.  Put the following at the start of the usercode:\n\n::\n\n    import math\n\n    points_per_orbit = 700\n\nThe first of these lines is just because we're going to need the Python ``math`` library in a moment; the second, as you might expect, is because we're going to plot 700 points around Mercury's orbital path.  This means that when we create arrays of data later, each array will have 700 points.\n\nNext, we need to work out how many days (or more accurately, what portion of a day) will elapse for each \"tick\" of an imaginary clock that ticks once for each of those points.  This value will be extremely useful later; to calculate it, put the following formula into cell B10: ``=B3 / points_per_orbit``.\n\nThere are three more simple conversions we need to do before we get on to the interesting stuff.  The various angles that are specified in the orbital parameters at the top of the sheet are all specified in human-friendly degrees -- we need to convert them into math-friendly radians.  To do that, enter the following formulae:\n\n* In B11, put ``=math.radians(B5)``\n* In B12, put ``=math.radians(B6)``\n* In B13, put ``=math.radians(B7)``\n\nYou'll wind up with something that looks like this:\n\n.. image:: tutorial-03-before-numpy.png\n\nSo, that's the dull bit done: on to the NumPy arrays!\n\n\nNumPy arrays in the grid\n^^^^^^^^^^^^^^^^^^^^^^^^\n\nAs our starting point, let's create a NumPy array with one element for each tick.  To do that, add code to import NumPy to the start of your usercode:\n\n::\n\n    import numpy\n\n...and then put the following formula into cell B15: ``=numpy.arange(points_per_orbit)``.  This will create a NumPy array containing the values 0, 1, 2, ... *points_per_orbit* - 1.  Dirigible will display it like this:\n\n.. image:: tutorial-03-numpy-arange.png\n\nThe next step is to find out what time it will be (in terms of days) when our imaginary clock reaches each tick.  For example, as there are about 0.12 days per tick, on the second tick the time will be 0.24 days.  To put it another way, we want an array that contains the values in the range that we created in cell B15, all multiplied by the days/tick value we put into cell B10.  The formula for that is just ``=B15 * B10``, so put that in cell B16.  The result should look like this:\n\n.. image:: tutorial-03-first-array-formula.png\n\nOf course, you can only see the first few values in the array -- but if you leave the mouse pointer \"hovering\" over the cell, you'll see a bubble help window with many more of them.\n\n.. image:: tutorial-03-bubble-array.png\n\nIt's worth reiterating what we've just done; with a simple formula, we've transformed a 700-element array into another one with different values, by applying the same function to each.  The NumPy library makes this easy to do in Python, and Dirigible makes it work inside a spreadsheet grid.\n\nThe next formula is similar in form; in order to calculate the *x*, *y* and *z* values, we need something called a `mean anomaly <http://en.wikipedia.org/wiki/Mean_anomaly>`_ at each point around the orbit.  The formula for this is again simple in form, and again involves creating a complete array of values: ``=(2 * math.pi * B16) / B3`` should go into B17.\n\n.. image:: tutorial-03-mean-anomalies.png\n\nSciPy, Newton-Raphson and transcendental equations\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nOur next step is a little more complex.  We want to work out the `eccentric anomaly <http://en.wikipedia.org/wiki/Eccentric_anomaly>`_ for each point in the orbit, but this is hard.  The equation that relates the mean anomaly *M*, the eccentric anomaly *E*, and the eccentricity *c* is this:\n\n::\n\n    M = E - c * sin E\n\nIf you can't see how to rearrange that so that it tells you how to get *E*, given values for *M* and *c*, you're not alone. The equation is `transcendental <http://en.wikipedia.org/wiki/Transcendental_equation>`_, and there is no simple solution. Instead, we have to use a numerical method to work out a best-guess value for *E*.  The great thing about Python is that there's already a package to do it all for us.  The ``scipy.optimize.newton`` function (`documented here <http://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.newton.html>`_) uses the `Newton-Raphson method <http://en.wikipedia.org/wiki/Newton%27s_method>`_ to find solutions to this type of equation.  You pass it a function (we'll call it ``f``) that takes a candidate solution and returns a value that converges on zero as the candidate solution converges on the correct result, along with another function that is the first derivative of ``f`` with respect to the candidate solution variable (we can call this one ``f_prime``).  The ``newton`` function does the work, and returns a solution that matches the requirements.  (You can specify other parameters to say how precise you want the result to be, but we won't go into that here.  The default settings are OK for our purposes.)\n\nSo, putting that all together, rearranging the equation above, translating it into Python, and using the ``newton`` function gives us the following way to calculate an eccentric anomaly, given a mean anomaly and an eccentricity:\n\n::\n\n    import scipy.optimize\n\n    def calculate_eccentric_anomaly(mean_anomaly, eccentricity):\n\n        def f(eccentric_anomaly_guess):\n            return eccentric_anomaly_guess - eccentricity * math.sin(eccentric_anomaly_guess) - mean_anomaly\n\n        def f_prime(eccentric_anomaly_guess):\n            return 1 - eccentricity * math.cos(eccentric_anomaly_guess)\n\n        return scipy.optimize.newton(f, mean_anomaly, f_prime)\n\nThat code (which you should put into your usercode somewhere above the call to ``evaluate_formulae``) handles just one eccentric anomaly.  We're dealing with arrays of values, not single values, so we need a version of the which will work with an array.  NumPy provides a convenient way of extending a function that works on just one value to one that works on an entire NumPy array: a function called ``vectorize`` (`whose documentation is here <http://docs.scipy.org/doc/numpy/reference/generated/numpy.vectorize.html>`_).  The code below (which you should put into the usercode too) uses ``vectorize`` on our ``calculate_eccentric_anomaly`` function, and then applies the resulting function to an array of mean anomalies given a single eccentricity:\n\n::\n\n    def calculate_eccentric_anomalies(eccentricity, mean_anomalies):\n        def _calculate_one_ecc_anom(mean_anomaly):\n            return calculate_eccentric_anomaly(mean_anomaly, eccentricity)\n\n        vectorized_calculate = numpy.vectorize(_calculate_one_ecc_anom)\n        return vectorized_calculate(mean_anomalies)\n\nSo, with all of that code put into the usercode editor, you should now be able to add a formula to cell B18 that uses them: ``=calculate_eccentric_anomalies(B2, B17)``.\n\n.. image:: tutorial-03-eccentric-anomalies.png\n\nGetting to the final numbers\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nThe remaining formulae are fairly simple, at least in terms of what they do -- all of them are fairly basic trigonometric manipulations of the values we've generated so far.  The only interesting thing about them is that we have to be careful to use the NumPy versions of the ``sin``, ``cos`` and ``tan`` functions when working on NumPy arrays, while we can use the normal Python ``math`` versions for single values.\n\n* Firstly we calculate the array of `true anomalies <http://en.wikipedia.org/wiki/True_anomaly>`_ in cell B19, using the formula ``=2 * numpy.arctan(math.sqrt((1 + B2) / (1 - B2)) * numpy.tan(B18 / 2))``\n* Next, we get the radius -- that is, Mercury's distance from the Sun -- at each point, by putting the formula ``=B4 * (1 - B2 ** 2) / (1 + B2 * numpy.cos(B19))`` into cell B20\n* Finally, we calculate the arrays of *x* positions, *y* positions, and *z* positions that are our goal, using these formulae:\n    * B23: ``=B20 * (math.cos(B12) * numpy.cos(B13 + B19) - math.sin(B12) * numpy.sin(B13 + B19) * math.cos(B11))``\n    * B24: ``=B20 * (math.sin(B12) * numpy.cos(B13 + B19) + math.cos(B12) * numpy.sin(B13 + B19) * math.cos(B11))``\n    * B25: ``=B20 * (numpy.sin(B13 + B19) * math.sin(B11))``\n\nWhen you're done, you should have a sheet that looks like this:\n\n.. image:: tutorial-03-all-done.png\n\nIf you let the mouse hover over cell B23-B25, you can see the raw values.\n\n\nIn conclusion\n^^^^^^^^^^^^^\n\nIn this part of the tutorial, we've worked through a spreadsheet that uses NumPy and SciPy, and Dirigible's ability to keep Python objects like NumPy arrays in cells, in order to use relatively simple formulae to calculate the movement of the planet Mercury around the Sun.  To put it another way -- we've calculated 700 *x* values, 700 *y* values, 700 *z* values and 700 time values, for a total of 2,800 numbers coming out of our spreadsheet, using just 13 formulae.  Doing it in a more traditional \"spreadsheety\" fashion, with just one number in each cell, would have required many thousands of cells with copied and pasted formulae -- which would have been significantly more error-prone and much harder to maintain.\n\nIn the :doc:`the next part of the tutorial <tutorial04>`, we show how you can use Dirigible to run nine copies of this spreadsheet in parallel -- one for each planet (including Pluto).  Each copy is automatically run on a separate machine in the Dirigible cloud, so calculating all nine orbits takes little more time than calculating just one!\n"
  },
  {
    "path": "documentation/tutorial04.rst",
    "content": "Tutorial part 4: Making your spreadsheets run in parallel\n=========================================================\n\nWhen you run a Dirigible spreadsheet, the Python code that is executed runs on specially-secured and programmed Internet servers.  The Dirigible network is made up of quite a number of these servers -- the exact number at any point depends on how busy our users are, as we scale up and down to match demand.  Normally, this is all transparent to you, but there is a way to use it to your advantage.\n\nIf you run several spreadsheets at the same time, then they will run on different servers -- so you basically have a number of computers working for you.   This means that if you have a problem that can be easily split up into different parts, each of which can be expressed as a separate spreadsheet, then you can run the different tasks in parallel and get the result you want faster.\n\nDirigible's :func:`run_worksheet` function makes this particularly easy, by allowing you to \"call\" one spreadsheet from another, in effect asking it to plug in certain starting values, to run itself, and to return a :class:`Worksheet` object containing the results.\n\nIn this part of the tutorial, we take the spreadsheet we created in :doc:`the last part <tutorial03>`, which calculated the orbit of the planet Mercury, and show how you can build another sheet that calls the first one nine times, to calculate the orbits of all of the planets (and Pluto) in little more time than it previously took to calculate just one.\n\nWe strongly recommend that you create your own sheet by working through this tutorial.\n\nLoading some basic data\n^^^^^^^^^^^^^^^^^^^^^^^\n\nThe first step is to get the basic orbital parameters -- the eccentricity, the orbital period, the semimajor axis, the inclination to the Sun's equator, the longitude of the ascending note and the argument of perihelion -- for each of the planets into a spreadsheet.  To save you from having to type these in, I've put them in a CSV file: `download it from here <https://s3.amazonaws.com/planet-data/all-planets.csv>`_, then create a new Dirigible worksheet, click on cell A1, and the \"Import\" button in the toolbar:\n\n.. image:: tutorial-03-csv-import-button.png\n\nOnce the file's uploaded, adjust the column widths so that you can see the numbers, and give the sheet a name -- you'll end up with something like this:\n\n.. image:: tutorial-04-after-csv-import.png\n\nThe next step is to add formulae that will send these values off to our spreadsheet from last time for evaluation.\n\nIntroducing run_worksheet\n^^^^^^^^^^^^^^^^^^^^^^^^^\n\nThe :func:`run_worksheet` function is simple to use, but very powerful.  It tells Dirigible to temporarily plug a set of values into another Dirigible spreadsheet, recalculate it, and return the results to you.  It's important to remember that it doesn't change the other spreadsheet while doing this -- the values you provide are temporary, and only used for the purposes of getting results from :func:`run_worksheet`.\n\nIt takes two arguments:\n\n* The URL of the target worksheet -- that is, the one you want to delegate work to.  In this case, this will be the URL of the spreadsheet you created in the last tutorial, something like ``http://localhost:8000/user/your-username/sheet/some-number/``\n* A dictionary of overrides -- the values you want to plug in to the other spreadsheet temporarily.  An example: ``{ (2, 3) : 25 }`` would tell :func:`run_worksheet` to override the original value of cell B3 with the value 23.\n\nIt returns a single object -- a :class:`Worksheet`, which you can access using the same methods as you would normally use with the built-in :const:`worksheet` variable when accessing it from usercode (for example, in the section :ref:`Generating formulae from usercode <generating-formulae>` in the first tutorial).\n\nUsing run_worksheet for one planet\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nSo, let's add a formula to use this to work out the orbital values for Pluto.  In cell I1, put \"Remote worksheet\" as a column header, and then in cell I10 enter the following formula (changing the URL appropriately):\n\n::\n\n  =run_worksheet(\"http://localhost:8000/user/your-username/sheet/some-number/\", { (2, 2) -> B10, (2, 3) -> C10, (2, 4) -> E10, (2, 5) -> F10, (2, 6) -> G10, (2, 7) -> H10 })\n\nDon't worry, we're going to tidy that up in a minute.  Once you've finished entering the formula, and once the \"recalculating\" animation next to the toolbar stops spinning, you should see something like this:\n\n.. image:: tutorial-04-first-formula.png\n\nCell I10 holds a worksheet object, which contains the results of plotting the orbit of the planet Pluto.  By default it just displays as something like ``<Worksheet worksheet-name>``.  To get the data out of it, we just need to treat it like a normal :class:`Worksheet`.  As an example, let's try pulling out the \"Days/tick\" value, which was in cell B10 in the other sheet.  Put ``=I10.B10.value`` into cell J10, and you should see something like this:\n\n.. image:: tutorial-04-access-numerical-data.png\n\nSo, for Pluto to go 1/700th of its distant, slow orbit, it takes over 129 days.  That sounds about right.  So, the next step is obviously to start extracting the useful data from the other sheet: the the lists of *x*, *y*, *z* and *t* values that specify the orbital plot.  As you may remember, these are in cells B23, B24, B25, and B16 in the original orbital plot sheet.  Let's put the values for Pluto into columns J, K, L and M.\n\nFirst, do the column headers:\n\n* Set J1 to ``x values``.\n* Set K1 to ``y values``.\n* Set L1 to ``z values``.\n* Set M1 to ``t values``.\n\nNext, extract the lists of values from the :class:`Worksheet` object in I10.  These won't initially work, but it's instructive to see why:\n\n* Set J10 to ``=I10.B23.value``.\n* Set K10 to ``=I10.B24.value``.\n* Set L10 to ``=I10.B25.value``.\n* Set M10 to ``=I10.B16.value``.\n\nYou should see that the formulae don't evaluate to anything useful -- specifically, to the Python value ``None``. Dirigible will indicate this to you by showing the values in grey:\n\n.. image:: tutorial-04-empty-lists.png\n\nThe reason for this lies in a detail of :func:`run_worksheet` -- it sends data between sheets using the `JSON format <http://www.json.org/>`_, which means that numbers, strings and simple lists are transferred transparently, but more complex data like Python objects, functions, and NumPy arrays need a little bit of help.  (This situation is likely to be improved in the future.)\n\nSo, for now, we need to alter our original sheet so that it produces objects that can be transferred over JSON.  Python lists of numbers are fine for this, so go **back to your sheet from the last tutorial**, and wrap ``list(...)`` around the formulae in cells B23, B24, and B25, so that (for example) B23 has a formula like this:\n\n::\n\n    =list(B20 * (math.cos(B12) * numpy.cos(B13 + B19) - math.sin(B12) * numpy.sin(B13 + B19) * math.cos(B11)))\n\nNow, the formula in B16 used to calculate the values for *t* needs to remain a NumPy array, as otherwise the formulae below it wouldn't work.  So let's add a new row 26 to the outputs section at the bottom of the grid, where we list-ify the *t* values:\n\n* Set A26 to ``ts``\n* Set B27 to ``=list(b16)``\n\nYou should now have something like this:\n\n.. image:: tutorial-04-listified-orbital-data.png\n\nNow, go back to your new sheet, hit refresh, and once the \"recalculating\" animation next to the toolbar stops spinning, you should get values for the *x*, *y* and *z* values:\n\n.. image:: tutorial-04-no-t-values-yet.png\n\nFinally, adjust the formula in M10 to reflect the new position of the *t* values, setting it to ``=I10.B26.value``.  You should now have all of the data in your grid:\n\n.. image:: tutorial-04-all-values-one-planet.png\n\nScaling to nine planets\n^^^^^^^^^^^^^^^^^^^^^^^\n\nNow, we could just take the contents of cells I10:M10 and duplicate them across the equivalent columns for the other planets, and this would be easy with copy and paste.  But let's tidy things up a bit first.  The formula in cell I10 is particularly long and complicated, and it would be extremely annoying if it needed to be changed -- for instance, if the cells in our single-planet calculation sheet moved around.  Let's move it into usercode.  First, we put a function right at the start, above the default usercode, that will call :func:`run_worksheet` given the appropriate parameters, and will return an object that encapsulates the information we're interested in:\n\n::\n\n    class OrbitValues(object):\n        def __init__(self, xs, ys, zs, ts):\n            self.xs = xs\n            self.ys = ys\n            self.zs = zs\n            self.ts = ts\n\n    orbit_calculator_url = \"http://localhost:8000/user/your-username/sheet/some-number/\"\n    def calculate_orbit(eccentricity, orbital_period, semimajor_axis, inclination, longitude_asc_node, arg_perihelion):\n        worksheet = run_worksheet(\n            orbit_calculator_url,\n            {\n                (2, 2): eccentricity,\n                (2, 3): orbital_period,\n                (2, 4): semimajor_axis,\n                (2, 5): inclination,\n                (2, 6): longitude_asc_node,\n                (2, 7): arg_perihelion\n            }\n        )\n        return OrbitValues(\n            worksheet[2, 23].value,\n            worksheet[2, 24].value,\n            worksheet[2, 25].value,\n            worksheet[2, 26].value,\n        )\n\nOnce again, you'll need to replace the ``orbit_calculator_url`` with the URL of your spreadsheet from the previous tutorial; you can copy this from the formula in cell I10.\n\nAs you can see, this new usercode is little more than a tided-up, neatly-formatted version of the formula we had previously -- but it's much easier to read and maintain.  Next, we adjust the formula in cell I10 to use it:\n\n::\n\n  =calculate_orbit(B10, C10, E10, F10, G10, H10)\n\nAnd finally, we change the formulae in cells J10:M10 to access the appropriate fields on our ``OrbitValues`` object\n\n* Set J10 to ``=I10.xs``.\n* Set K10 to ``=I10.ys``.\n* Set L10 to ``=I10.zs``.\n* Set M10 to ``=I10.ts``.\n\nYou'll wind up with something like this:\n\n.. image:: tutorial-04-all-values-one-planet-refactored.png\n\nNow we've got a more-maintainable formula, let's copy and paste it.  Select cells I10:M10, hit Control-C (Command-C if you're on a Mac), then select A1:M9, and paste with Control/Command-V.  Wait for a few moments, and you should see the values filled in!\n\n.. image:: tutorial-04-all-planets.png\n\nEven better, while it will probably have taken noticably longer to calculate the orbits of nine planets then it did to calculate one, it will not have taken nine times as long; depending on the current state of the Dirigible grid, it is likely to have taken between 1.5 and 3 times as long.\n\nHow does this work?\n^^^^^^^^^^^^^^^^^^^\n\nWhen Dirigible executes the :func:`evaluate_formulae` statement in your usercode to calculate results from the formulae that you've specified in the grid, it analyses your spreadsheet and works out which cells depend on which other cells.  It is able to use this information to work out which cells can be calculated in parallel.\n\nSo, because the various calls to :func:`run_worksheet` that are triggered via the ``calculate_orbit`` calls in cells I2:I10 do not depend on each other, Dirigible is able to run them all at the same time.  Because they are accessing another spreadsheet, each sub-calculation runs on a different machine within the Dirigible grid -- so for the duration of your spreadsheet's recalculation, you have up to ten computers working for you -- the one running the nine-planet sheet, and nine others running different versions of the single-planet sheet from the last part of the tutorial.\n\nDirigible's parallelisation of spreadsheet cell recalculation isn't tied to :func:`run_worksheet` -- any cells that can be run in parallel may be -- but it comes into its own when work can be offloaded to other computers like this.\n\nIn conclusion\n^^^^^^^^^^^^^\n\nIn this part of the tutorial, we've worked through a spreadsheet that uses :func:`run_worksheet` to delegate multiple invocations of another spreadsheet to other computers, showing how you can easily re-use Dirigible spreadsheets from other Dirigible spreadsheets and benefit from scaling your calculations across the grid.  Our complete system of two spreadsheets uses just 13 formulae in the original single-planet sheet, then four formulae replicated nine times in the second, for a total of 13 + 36 = 49 formulae, all of which are pretty simple, and yet it produces a total of 25,200 numbers calculated by ten computers working in concert.\n\nIn the next tutorial (coming soon), we'll put this data to use, by writing a simple web page that extracts the data from the spreadsheet and displays it graphically.\n"
  },
  {
    "path": "requirements.txt",
    "content": "Django==1.7\nargparse==1.2.1\nchardet==2.2.1\nmock==1.0.1\n# numpy==1.8.1\nply==3.4\nsimplejson==3.5.2\nwsgiref==0.1.2\nxlrd==0.9.3\nselenium\n"
  }
]