Replace django-selectable with selectize
authorMagnus Hagander <[email protected]>
Thu, 25 Nov 2021 12:30:28 +0000 (13:30 +0100)
committerMagnus Hagander <[email protected]>
Thu, 25 Nov 2021 12:30:28 +0000 (13:30 +0100)
django-selectable is no longer maintained, and will cause issues in
newer versions of django.

selectize is what we use in the pgeu-system codebase today, so copy the
handling over from there.

91 files changed:
dep/django-selectable/.coveragerc [deleted file]
dep/django-selectable/.tx/config [deleted file]
dep/django-selectable/AUTHORS.txt [deleted file]
dep/django-selectable/LICENSE.txt [deleted file]
dep/django-selectable/MANIFEST.in [deleted file]
dep/django-selectable/Makefile [deleted file]
dep/django-selectable/README.rst [deleted file]
dep/django-selectable/docs/Makefile [deleted file]
dep/django-selectable/docs/admin.rst [deleted file]
dep/django-selectable/docs/advanced.rst [deleted file]
dep/django-selectable/docs/conf.py [deleted file]
dep/django-selectable/docs/contribute.rst [deleted file]
dep/django-selectable/docs/fields.rst [deleted file]
dep/django-selectable/docs/index.rst [deleted file]
dep/django-selectable/docs/lookups.rst [deleted file]
dep/django-selectable/docs/make.bat [deleted file]
dep/django-selectable/docs/overview.rst [deleted file]
dep/django-selectable/docs/quick-start.rst [deleted file]
dep/django-selectable/docs/releases.rst [deleted file]
dep/django-selectable/docs/settings.rst [deleted file]
dep/django-selectable/docs/testing.rst [deleted file]
dep/django-selectable/docs/widgets.rst [deleted file]
dep/django-selectable/runtests.py [deleted file]
dep/django-selectable/selectable/__init__.py [deleted file]
dep/django-selectable/selectable/apps.py [deleted file]
dep/django-selectable/selectable/base.py [deleted file]
dep/django-selectable/selectable/compat.py [deleted file]
dep/django-selectable/selectable/decorators.py [deleted file]
dep/django-selectable/selectable/exceptions.py [deleted file]
dep/django-selectable/selectable/forms/__init__.py [deleted file]
dep/django-selectable/selectable/forms/base.py [deleted file]
dep/django-selectable/selectable/forms/fields.py [deleted file]
dep/django-selectable/selectable/forms/widgets.py [deleted file]
dep/django-selectable/selectable/locale/en/LC_MESSAGES/django.mo [deleted file]
dep/django-selectable/selectable/locale/en/LC_MESSAGES/django.po [deleted file]
dep/django-selectable/selectable/locale/es/LC_MESSAGES/django.mo [deleted file]
dep/django-selectable/selectable/locale/es/LC_MESSAGES/django.po [deleted file]
dep/django-selectable/selectable/locale/fr/LC_MESSAGES/django.mo [deleted file]
dep/django-selectable/selectable/locale/fr/LC_MESSAGES/django.po [deleted file]
dep/django-selectable/selectable/locale/pl/LC_MESSAGES/django.mo [deleted file]
dep/django-selectable/selectable/locale/pl/LC_MESSAGES/django.po [deleted file]
dep/django-selectable/selectable/locale/pt_BR/LC_MESSAGES/django.mo [deleted file]
dep/django-selectable/selectable/locale/pt_BR/LC_MESSAGES/django.po [deleted file]
dep/django-selectable/selectable/locale/zh_CN/LC_MESSAGES/django.mo [deleted file]
dep/django-selectable/selectable/locale/zh_CN/LC_MESSAGES/django.po [deleted file]
dep/django-selectable/selectable/models.py [deleted file]
dep/django-selectable/selectable/registry.py [deleted file]
dep/django-selectable/selectable/static/selectable/css/dj.selectable.css [deleted file]
dep/django-selectable/selectable/static/selectable/js/jquery.dj.selectable.js [deleted file]
dep/django-selectable/selectable/templates/selectable/jquery-css.html [deleted file]
dep/django-selectable/selectable/templates/selectable/jquery-js.html [deleted file]
dep/django-selectable/selectable/templatetags/__init__.py [deleted file]
dep/django-selectable/selectable/templatetags/selectable_tags.py [deleted file]
dep/django-selectable/selectable/tests/__init__.py [deleted file]
dep/django-selectable/selectable/tests/base.py [deleted file]
dep/django-selectable/selectable/tests/qunit/helpers.js [deleted file]
dep/django-selectable/selectable/tests/qunit/index.html [deleted file]
dep/django-selectable/selectable/tests/qunit/jquery-loader.js [deleted file]
dep/django-selectable/selectable/tests/qunit/main.js [deleted file]
dep/django-selectable/selectable/tests/qunit/sinon-1.5.2.js [deleted file]
dep/django-selectable/selectable/tests/qunit/test-events.js [deleted file]
dep/django-selectable/selectable/tests/qunit/test-methods.js [deleted file]
dep/django-selectable/selectable/tests/qunit/test-options.js [deleted file]
dep/django-selectable/selectable/tests/test_base.py [deleted file]
dep/django-selectable/selectable/tests/test_decorators.py [deleted file]
dep/django-selectable/selectable/tests/test_fields.py [deleted file]
dep/django-selectable/selectable/tests/test_forms.py [deleted file]
dep/django-selectable/selectable/tests/test_functional.py [deleted file]
dep/django-selectable/selectable/tests/test_templatetags.py [deleted file]
dep/django-selectable/selectable/tests/test_views.py [deleted file]
dep/django-selectable/selectable/tests/test_widgets.py [deleted file]
dep/django-selectable/selectable/tests/urls.py [deleted file]
dep/django-selectable/selectable/tests/views.py [deleted file]
dep/django-selectable/selectable/urls.py [deleted file]
dep/django-selectable/selectable/views.py [deleted file]
dep/django-selectable/setup.cfg [deleted file]
dep/django-selectable/setup.py [deleted file]
dep/django-selectable/tox.ini [deleted file]
pgcommitfest/commitfest/forms.py
pgcommitfest/commitfest/lookups.py
pgcommitfest/commitfest/static/commitfest/css/commitfest.css
pgcommitfest/commitfest/static/commitfest/css/selectize.css [new file with mode: 0644]
pgcommitfest/commitfest/static/commitfest/css/selectize.default.css [new file with mode: 0644]
pgcommitfest/commitfest/static/commitfest/js/selectize.min.js [new file with mode: 0644]
pgcommitfest/commitfest/templates/base.html
pgcommitfest/commitfest/templates/base_form.html
pgcommitfest/commitfest/views.py
pgcommitfest/selectable [deleted symlink]
pgcommitfest/settings.py
pgcommitfest/urls.py
selectable [deleted symlink]

diff --git a/dep/django-selectable/.coveragerc b/dep/django-selectable/.coveragerc
deleted file mode 100644 (file)
index 085d731..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-[run]
-source = selectable
-omit = */tests*,*/urls.py
diff --git a/dep/django-selectable/.tx/config b/dep/django-selectable/.tx/config
deleted file mode 100644 (file)
index abd6010..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-[main]
-host = https://www.transifex.com
-
-[django-selectable.txo]
-file_filter = selectable/locale/<lang>/LC_MESSAGES/django.po
-source_file = selectable/locale/en/LC_MESSAGES/django.po
-source_lang = en
-type = PO
diff --git a/dep/django-selectable/AUTHORS.txt b/dep/django-selectable/AUTHORS.txt
deleted file mode 100644 (file)
index ef1920f..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-Primary author:
-
-Mark Lavin
-
-The following people who have contributed to django-selectable:
-
-Michael Manfre
-Luke Plant
-Augusto Men
-@dc
-Colin Copeland
-Sławomir Ehlert
-Dan Poirier
-Felipe Prenholato
-David Ray
-Rick Testore
-Karen Tracey
-Manuel Alvarez
-Ustun Ozgur
-@leo-the-manic
-Calvin Spealman
-Rebecca Lovewell
-Thomas Güttler
-Yuri Khrustalev
-@SaeX
-Tam Huynh
-Raphael Merx
-Josh Addington
-Tobias Zanke
-Petr Dlouhy
-Vinod Kurup
-
-Thanks for all of your work!
diff --git a/dep/django-selectable/LICENSE.txt b/dep/django-selectable/LICENSE.txt
deleted file mode 100644 (file)
index 41cda46..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-Copyright (c) 2010-201999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999, Mark Lavin
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification,
-are permitted provided that the following conditions are met:
-
-* Redistributions of source code must retain the above copyright notice,
-this list of conditions and the following disclaimer.
-
-* Redistributions in binary form must reproduce the above copyright notice,
-this list of conditions and the following disclaimer in the documentation
-and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
-ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
-ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/dep/django-selectable/MANIFEST.in b/dep/django-selectable/MANIFEST.in
deleted file mode 100644 (file)
index ce396ae..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-include AUTHORS.txt
-include README.rst
-include LICENSE.txt
-recursive-include selectable/locale *
-recursive-include selectable/static *
-recursive-include selectable/templates *
diff --git a/dep/django-selectable/Makefile b/dep/django-selectable/Makefile
deleted file mode 100644 (file)
index d82053a..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-STATIC_DIR = ./selectable/static/selectable
-QUNIT_TESTS = file://`pwd`/selectable/tests/qunit/index.html
-
-test-js:
-       # Run JS tests
-       # Requires phantomjs
-       phantomjs run-qunit.js ${QUNIT_TESTS}?jquery=1.11.2&ui=1.11.4
-       phantomjs run-qunit.js ${QUNIT_TESTS}?jquery=1.11.2&ui=1.10.4
-       phantomjs run-qunit.js ${QUNIT_TESTS}?jquery=1.10.2&ui=1.11.4
-       phantomjs run-qunit.js ${QUNIT_TESTS}?jquery=1.10.2&ui=1.10.4
-       phantomjs run-qunit.js ${QUNIT_TESTS}?jquery=1.9.1&ui=1.11.4
-       phantomjs run-qunit.js ${QUNIT_TESTS}?jquery=1.9.1&ui=1.10.4
-
-
-lint-js:
-       # Check JS for any problems     
-       # Requires jshint
-       jshint ${STATIC_DIR}/js/jquery.dj.selectable.js
-
-
-.PHONY: lint-js test-js
diff --git a/dep/django-selectable/README.rst b/dep/django-selectable/README.rst
deleted file mode 100644 (file)
index 8a06eb3..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-django-selectable
-===================
-
-Tools and widgets for using/creating auto-complete selection widgets using Django and jQuery UI.
-
-.. image:: https://travis-ci.org/mlavin/django-selectable.svg?branch=master
-    :target: https://travis-ci.org/mlavin/django-selectable
-
-.. image:: https://codecov.io/github/mlavin/django-selectable/coverage.svg?branch=master
-    :target: https://codecov.io/github/mlavin/django-selectable?branch=master
-
-
-.. note::
-
-    This project is looking for additional maintainers to help with Django/jQuery compatibility
-    issues as well as addressing support issues/questions. If you are looking to help out
-    on this project and take a look at the open
-    `help-wanted <https://github.com/mlavin/django-selectable/issues?q=is%3Aissue+is%3Aopen+label%3Ahelp-wanted>`_
-    or `question <https://github.com/mlavin/django-selectable/issues?q=is%3Aissue+is%3Aopen+label%3Aquestion>`_
-    and see if you can contribute a fix. Be bold! If you want to take a larger role on
-    the project, please reach out on the
-    `mailing list <http://groups.google.com/group/django-selectable>`_. I'm happy to work
-    with you to get you going on an issue.
-
-
-Features
------------------------------------
-
-- Works with the latest jQuery UI Autocomplete library
-- Auto-discovery/registration pattern for defining lookups
-
-
-Installation Requirements
------------------------------------
-
-- Python 2.7, 3.4+
-- `Django <http://www.djangoproject.com/>`_ >= 1.11, <= 3.0
-- `jQuery <http://jquery.com/>`_ >= 1.9, < 3.0
-- `jQuery UI <http://jqueryui.com/>`_ >= 1.10
-
-To install::
-
-    pip install django-selectable
-
-Next add `selectable` to your `INSTALLED_APPS` to include the related css/js::
-
-    INSTALLED_APPS = (
-        'contrib.staticfiles',
-        # Other apps here
-        'selectable',
-    )
-
-The jQuery and jQuery UI libraries are not included in the distribution but must be included
-in your templates. See the example project for an example using these libraries from the
-Google CDN.
-
-Once installed you should add the urls to your root url patterns::
-
-    urlpatterns = [
-        # Other patterns go here
-        url(r'^selectable/', include('selectable.urls')),
-    ]
-
-
-Documentation
------------------------------------
-
-Documentation for django-selectable is available on `Read The Docs <http://django-selectable.readthedocs.io/en/latest/>`_.
-
-
-Additional Help/Support
------------------------------------
-
-You can find additional help or support on the mailing list: http://groups.google.com/group/django-selectable
-
-
-Contributing
---------------------------------------
-
-If you think you've found a bug or are interested in contributing to this project
-check out our `contributing guide <http://readthedocs.org/docs/django-selectable/en/latest/contribute.html>`_.
-
-If you are interested in translating django-selectable into your native language
-you can join the `Transifex project <https://www.transifex.com/projects/p/django-selectable/>`_.
diff --git a/dep/django-selectable/docs/Makefile b/dep/django-selectable/docs/Makefile
deleted file mode 100644 (file)
index 87770d9..0000000
+++ /dev/null
@@ -1,130 +0,0 @@
-# Makefile for Sphinx documentation
-#
-
-# You can set these variables from the command line.
-SPHINXOPTS    =
-SPHINXBUILD   = sphinx-build
-PAPER         =
-BUILDDIR      = _build
-
-# Internal variables.
-PAPEROPT_a4     = -D latex_paper_size=a4
-PAPEROPT_letter = -D latex_paper_size=letter
-ALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
-
-.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
-
-help:
-       @echo "Please use \`make <target>' where <target> is one of"
-       @echo "  html       to make standalone HTML files"
-       @echo "  dirhtml    to make HTML files named index.html in directories"
-       @echo "  singlehtml to make a single large HTML file"
-       @echo "  pickle     to make pickle files"
-       @echo "  json       to make JSON files"
-       @echo "  htmlhelp   to make HTML files and a HTML help project"
-       @echo "  qthelp     to make HTML files and a qthelp project"
-       @echo "  devhelp    to make HTML files and a Devhelp project"
-       @echo "  epub       to make an epub"
-       @echo "  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
-       @echo "  latexpdf   to make LaTeX files and run them through pdflatex"
-       @echo "  text       to make text files"
-       @echo "  man        to make manual pages"
-       @echo "  changes    to make an overview of all changed/added/deprecated items"
-       @echo "  linkcheck  to check all external links for integrity"
-       @echo "  doctest    to run all doctests embedded in the documentation (if enabled)"
-
-clean:
-       -rm -rf $(BUILDDIR)/*
-
-html:
-       $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
-       @echo
-       @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
-
-dirhtml:
-       $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
-       @echo
-       @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
-
-singlehtml:
-       $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
-       @echo
-       @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
-
-pickle:
-       $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
-       @echo
-       @echo "Build finished; now you can process the pickle files."
-
-json:
-       $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
-       @echo
-       @echo "Build finished; now you can process the JSON files."
-
-htmlhelp:
-       $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
-       @echo
-       @echo "Build finished; now you can run HTML Help Workshop with the" \
-             ".hhp project file in $(BUILDDIR)/htmlhelp."
-
-qthelp:
-       $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
-       @echo
-       @echo "Build finished; now you can run "qcollectiongenerator" with the" \
-             ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
-       @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Django-Selectable.qhcp"
-       @echo "To view the help file:"
-       @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Django-Selectable.qhc"
-
-devhelp:
-       $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
-       @echo
-       @echo "Build finished."
-       @echo "To view the help file:"
-       @echo "# mkdir -p $$HOME/.local/share/devhelp/Django-Selectable"
-       @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Django-Selectable"
-       @echo "# devhelp"
-
-epub:
-       $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
-       @echo
-       @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
-
-latex:
-       $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
-       @echo
-       @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
-       @echo "Run \`make' in that directory to run these through (pdf)latex" \
-             "(use \`make latexpdf' here to do that automatically)."
-
-latexpdf:
-       $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
-       @echo "Running LaTeX files through pdflatex..."
-       make -C $(BUILDDIR)/latex all-pdf
-       @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
-
-text:
-       $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
-       @echo
-       @echo "Build finished. The text files are in $(BUILDDIR)/text."
-
-man:
-       $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
-       @echo
-       @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
-
-changes:
-       $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
-       @echo
-       @echo "The overview file is in $(BUILDDIR)/changes."
-
-linkcheck:
-       $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
-       @echo
-       @echo "Link check complete; look for any errors in the above output " \
-             "or in $(BUILDDIR)/linkcheck/output.txt."
-
-doctest:
-       $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
-       @echo "Testing of doctests in the sources finished, look at the " \
-             "results in $(BUILDDIR)/doctest/output.txt."
diff --git a/dep/django-selectable/docs/admin.rst b/dep/django-selectable/docs/admin.rst
deleted file mode 100644 (file)
index df64701..0000000
+++ /dev/null
@@ -1,173 +0,0 @@
-Admin Integration
-====================
-
-Overview
---------------------------------------
-
-Django-Selectables will work in the admin. To get started on integrated the
-fields and widgets in the admin make sure you are familiar with the Django
-documentation on the `ModelAdmin.form <http://docs.djangoproject.com/en/stable/ref/contrib/admin/#django.contrib.admin.ModelAdmin.form>`_
-and `ModelForms <http://docs.djangoproject.com/en/stable/topics/forms/modelforms/>`_ particularly
-on `overriding the default widgets <http://docs.djangoproject.com/en/stable/topics/forms/modelforms/#overriding-the-default-field-types-or-widgets>`_.
-As you will see integrating django-selectable in the adminis the same as working with regular forms.
-
-
-.. _admin-jquery-include:
-
-Including jQuery & jQuery UI
---------------------------------------
-
-As noted :ref:`in the quick start guide <start-include-jquery>`, the jQuery and jQuery UI libraries
-are not included in the distribution but must be included in your templates. For the
-Django admin that means overriding
-`admin/base_site.html <https://code.djangoproject.com/browser/django/trunk/django/contrib/admin/templates/admin/base_site.html>`_.
-You can include this media in the block name `extrahead` which is defined in
-`admin/base.html <https://code.djangoproject.com/browser/django/trunk/django/contrib/admin/templates/admin/base.html>`_.
-
-    .. code-block:: html
-
-        {% block extrahead %}
-            {% load selectable_tags %}
-            {% include_ui_theme %}
-            {% include_jquery_libs %}
-            {{ block.super }}
-        {% endblock %}
-
-See the Django documentation on
-`overriding admin templates <https://docs.djangoproject.com/en/stable/ref/contrib/admin/#overriding-admin-templates>`_.
-See the example project for the full template example.
-
-
-.. _admin-grappelli:
-
-Using Grappelli
---------------------------------------
-
-`Grappelli <https://django-grappelli.readthedocs.org>`_ is a popular customization of the Django
-admin interface. It includes a number of interface improvements which are also built on top of
-jQuery UI. When using Grappelli you do not need to make any changes to the ``admin/base_site.html``
-template. django-selectable will detect jQuery and jQuery UI versions included by Grappelli
-and make use of them.
-
-
-.. _admin-basic-example:
-
-Basic Example
---------------------------------------
-
-For example, we may have a ``Farm`` model with a foreign key to ``auth.User`` and
-a many to many relation to our ``Fruit`` model.
-
-    .. code-block:: python
-
-        from __future__ import unicode_literals
-
-        from django.db import models
-        from django.utils.encoding import python_2_unicode_compatible
-
-
-        @python_2_unicode_compatible
-        class Fruit(models.Model):
-            name = models.CharField(max_length=200)
-
-            def __str__(self):
-                return self.name
-
-
-        @python_2_unicode_compatible
-        class Farm(models.Model):
-            name = models.CharField(max_length=200)
-            owner = models.ForeignKey('auth.User', related_name='farms', on_delete=models.CASCADE)
-            fruit = models.ManyToManyField(Fruit)
-
-            def __str__(self):
-                return "%s's Farm: %s" % (self.owner.username, self.name)
-
-In `admin.py` we will define the form and associate it with the `FarmAdmin`.
-
-    .. code-block:: python
-
-        from django.contrib import admin
-        from django.contrib.auth.admin import UserAdmin
-        from django.contrib.auth.models import User
-        from django import forms
-
-        from selectable.forms import AutoCompleteSelectField, AutoCompleteSelectMultipleWidget
-
-        from .models import Fruit, Farm
-        from .lookups import FruitLookup, OwnerLookup
-
-
-        class FarmAdminForm(forms.ModelForm):
-            owner = AutoCompleteSelectField(lookup_class=OwnerLookup, allow_new=True)
-
-            class Meta(object):
-                model = Farm
-                widgets = {
-                    'fruit': AutoCompleteSelectMultipleWidget(lookup_class=FruitLookup),
-                }
-                exclude = ('owner', )
-
-            def __init__(self, *args, **kwargs):
-                super(FarmAdminForm, self).__init__(*args, **kwargs)
-                if self.instance and self.instance.pk and self.instance.owner:
-                    self.initial['owner'] = self.instance.owner.pk
-
-            def save(self, *args, **kwargs):
-                owner = self.cleaned_data['owner']
-                if owner and not owner.pk:
-                    owner = User.objects.create_user(username=owner.username, email='')
-                self.instance.owner = owner
-                return super(FarmAdminForm, self).save(*args, **kwargs)
-
-
-        class FarmAdmin(admin.ModelAdmin):
-            form = FarmAdminForm
-
-
-        admin.site.register(Farm, FarmAdmin)
-
-
-You'll note this form also allows new users to be created and associated with the
-farm, if no user is found matching the given name. To make use of this feature we
-need to add ``owner`` to the exclude so that it will pass model validation. Unfortunately
-that means we must set the owner manual in the save and in the initial data because
-the ``ModelForm`` will no longer do this for you. Since ``fruit`` does not allow new
-items you'll see these steps are not necessary.
-
-The django-selectable widgets are compatitible with the add another popup in the
-admin. It's that little green plus sign that appears next to ``ForeignKey`` or
-``ManyToManyField`` items. This makes django-selectable a user friendly replacement
-for the `ModelAdmin.raw_id_fields <https://docs.djangoproject.com/en/stable/ref/contrib/admin/#django.contrib.admin.ModelAdmin.raw_id_fields>`_
-when the default select box grows too long.
-
-
-.. _admin-inline-example:
-
-Inline Example
---------------------------------------
-
-With our ``Farm`` model we can also associate the ``UserAdmin`` with a ``Farm``
-by making use of the `InlineModelAdmin
-<http://docs.djangoproject.com/en/stable/ref/contrib/admin/#inlinemodeladmin-objects>`_.
-We can even make use of the same ``FarmAdminForm``.
-
-    .. code-block:: python
-
-        # continued from above
-
-        class FarmInline(admin.TabularInline):
-            model = Farm
-            form = FarmAdminForm
-
-
-        class NewUserAdmin(UserAdmin):
-            inlines = [
-                FarmInline,
-            ]
-
-
-        admin.site.unregister(User)
-        admin.site.register(User, NewUserAdmin)
-
-The auto-complete functions will be bound as new forms are added dynamically.
diff --git a/dep/django-selectable/docs/advanced.rst b/dep/django-selectable/docs/advanced.rst
deleted file mode 100644 (file)
index d3ffe2b..0000000
+++ /dev/null
@@ -1,381 +0,0 @@
-Advanced Usage
-==========================
-
-We've gone through the most command and simple use cases for django-selectable. Now
-we'll take a look at some of the more advanced features of this project. This assumes
-that you are comfortable reading and writing a little bit of Javascript making
-use of jQuery.
-
-
-.. _additional-parameters:
-
-Additional Parameters
---------------------------------------
-
-The basic lookup is based on handling a search based on a single term string.
-If additional filtering is needed it can be inside the lookup ``get_query`` but
-you would need to define this when the lookup is defined. While this fits a fair
-number of use cases there are times when you need to define additional query
-parameters that won't be known until either the form is bound or until selections
-are made on the client side. This section will detail how to handle both of these
-cases.
-
-
-How Parameters are Passed
-_______________________________________
-
-As with the search term, the additional parameters you define will be passed in
-``request.GET``. Since ``get_query`` gets the current request, you will have access to
-them. Since they can be manipulated on the client side, these parameters should be
-treated like all user input. It should be properly validated and sanitized.
-
-
-Limiting the Result Set
-_______________________________________
-
-The number of results are globally limited/paginated by the :ref:`SELECTABLE_MAX_LIMIT`
-but you can also lower this limit on the field or widget level. Each field and widget
-takes a ``limit`` argument in the ``__init__`` that will be passed back to the lookup
-through the ``limit`` query parameter. The result set will be automatically paginated
-for you if you use either this parameter or the global setting.
-
-
-.. _server-side-parameters:
-
-Adding Parameters on the Server Side
-_______________________________________
-
-Each of the widgets define ``update_query_parameters`` which takes a dictionary. The
-most common way to use this would be in the form ``__init__``.
-
-    .. code-block:: python
-
-        class FruitForm(forms.Form):
-            autocomplete = forms.CharField(
-                label='Type the name of a fruit (AutoCompleteWidget)',
-                widget=selectable.AutoCompleteWidget(FruitLookup),
-                required=False,
-            )
-
-            def __init__(self, *args, **kwargs):
-                super(FruitForm, self).__init__(*args, **kwargs)
-                self.fields['autocomplete'].widget.update_query_parameters({'foo': 'bar'})
-
-You can also pass the query parameters into the widget using the ``query_params``
-keyword argument. It depends on your use case as to whether the parameters are
-known when the form is defined or when an instance of the form is created.
-
-
-.. _client-side-parameters:
-
-Adding Parameters on the Client Side
-_______________________________________
-
-There are times where you want to filter the result set based other selections
-by the user such as a filtering cities by a previously selected state. In this
-case you will need to bind a ``prepareQuery`` to the field. This function should accept the query dictionary.
-You are free to make adjustments to  the query dictionary as needed.
-
-    .. code-block:: html
-
-        <script type="text/javascript">
-            function newParameters(query) {
-                query.foo = 'bar';
-            }
-
-            $(document).ready(function() {
-                $('#id_autocomplete').djselectable('option', 'prepareQuery', newParameters);
-            });
-        </script>
-
-.. note::
-
-    In v0.7 the scope of ``prepareQuery`` was updated so that ``this`` refers to the
-    current ``djselectable`` plugin instance. Previously ``this`` refered to the
-    plugin ``options`` instance.
-
-
-.. _chain-select-example:
-
-Chained Selection
---------------------------------------
-
-It's a fairly common pattern to have two or more inputs depend one another such City/State/Zip.
-In fact there are other Django apps dedicated to this purpose such as
-`django-smart-selects <https://github.com/digi604/django-smart-selects>`_ or
-`django-ajax-filtered-fields <http://code.google.com/p/django-ajax-filtered-fields/>`_.
-It's possible to handle this kind of selection with django-selectable if you are willing
-to write a little javascript.
-
-Suppose we have city model
-
-    .. code-block:: python
-
-        from __future__ import unicode_literals
-
-        from django.db import models
-        from django.utils.encoding import python_2_unicode_compatible
-
-        from localflavor.us.models import USStateField
-
-
-        @python_2_unicode_compatible
-        class City(models.Model):
-            name = models.CharField(max_length=200)
-            state = USStateField()
-
-            def __str__(self):
-                return self.name
-
-Then in our lookup we will grab the state value and filter our results on it:
-
-    .. code-block:: python
-
-        from __future__ import unicode_literals
-
-        from selectable.base import ModelLookup
-        from selectable.registry import registry
-
-        from .models import City
-
-
-        class CityLookup(ModelLookup):
-            model = City
-            search_fields = ('name__icontains', )
-
-            def get_query(self, request, term):
-                results = super(CityLookup, self).get_query(request, term)
-                state = request.GET.get('state', '')
-                if state:
-                    results = results.filter(state=state)
-                return results
-
-            def get_item_label(self, item):
-                return "%s, %s" % (item.name, item.state)
-
-
-        registry.register(CityLookup)
-
-and a simple form
-
-    .. code-block:: python
-
-        from django import forms
-
-        from localflavor.us.forms import USStateField, USStateSelect
-
-        from selectable.forms import AutoCompleteSelectField, AutoComboboxSelectWidget
-
-        from .lookups import CityLookup
-
-
-        class ChainedForm(forms.Form):
-            city = AutoCompleteSelectField(
-                lookup_class=CityLookup,
-                label='City',
-                required=False,
-                widget=AutoComboboxSelectWidget
-            )
-            state = USStateField(widget=USStateSelect, required=False)
-
-
-We want our users to select a city and if they choose a state then we will only
-show them cities in that state. To do this we will pass back chosen state as
-addition parameter with the following javascript:
-
-    .. code-block:: html
-
-        <script type="text/javascript">
-            $(document).ready(function() {
-                function newParameters(query) {
-                    query.state = $('#id_state').val();
-                }
-                $('#id_city_0').djselectable('option', 'prepareQuery', newParameters);
-            });
-        </script>
-
-And that's it! We now have a working chained selection example. The full source
-is included in the example project.
-
-.. _client-side-changes:
-
-Detecting Client Side Changes
-____________________________________________
-
-The previous example detected selection changes on the client side to allow passing
-parameters to the lookup. Since django-selectable is built on top of the jQuery UI
-`Autocomplete plug-in <http://jqueryui.com/demos/autocomplete/>`_, the widgets
-expose the events defined by the plugin.
-
-    - djselectablecreate
-    - djselectablesearch
-    - djselectableopen
-    - djselectablefocus
-    - djselectableselect
-    - djselectableclose
-    - djselectablechange
-
-For the most part these event names should be self-explanatory. If you need additional
-detail you should refer to the `jQuery UI docs on these events <http://jqueryui.com/demos/autocomplete/#events>`_.
-
-The multiple select widgets include additional events which indicate when a new item is added
-or removed from the current list. These events are ``djselectableadd`` and ``djselectableremove``.
-These events pass a dictionary of data with the following keys
-
-    - element: The original text input
-    - input: The hidden input to be added for the new item
-    - wrapper: The ``<li>`` element to be added to the deck
-    - deck: The outer ``<ul>`` deck element
-
-You can use these events to prevent items from being added or removed from the deck by
-returning ``false`` in the handling function. A simple example is given below:
-
-    .. code-block:: html
-
-        <script type="text/javascript">
-            $(document).ready(function() {
-                $(':input[name=my_field_0]').bind('djselectableadd', function(event, item) {
-                    // Don't allow foo to be added
-                    if ($(item.input).val() === 'foo') {
-                        return false;
-                    }
-                });
-            });
-        </script>
-
-
-Submit On Selection
---------------------------------------
-
-You might want to help your users by submitting the form once they have selected a valid
-item. To do this you simply need to listen for the ``djselectableselect`` event. This
-event is fired by the text input which has an index of 0. If your field is named ``my_field``
-then input to watch would be ``my_field_0`` such as:
-
-    .. code-block:: html
-
-        <script type="text/javascript">
-            $(document).ready(function() {
-                $(':input[name=my_field_0]').bind('djselectableselect', function(event, ui) {
-                    $(this).parents("form").submit();
-                });
-            });
-        </script>
-
-
-Dynamically Added Forms
---------------------------------------
-
-django-selectable can work with dynamically added forms such as inlines in the admin.
-To make django-selectable work in the admin there is nothing more to do than include
-the necessary static media as described in the
-:ref:`Admin Integration <admin-jquery-include>` section.
-
-If you are making use of the popular `django-dynamic-formset <http://code.google.com/p/django-dynamic-formset/>`_
-then you can make django-selectable work by passing ``bindSelectables`` to the
-`added <http://code.google.com/p/django-dynamic-formset/source/browse/trunk/docs/usage.txt#259>`_ option:
-
-    .. code-block:: html
-
-        <script type="text/javascript">
-            $(document).ready(function() {
-                $('#my-formset').formset({
-                               added: bindSelectables
-                });
-            });
-        </script>
-
-Currently you must include the django-selectable javascript below this formset initialization
-code for this to work. See django-selectable `issue #31 <https://github.com/mlavin/django-selectable/issues/31>`_
-for some additional detail on this problem.
-
-
-.. _advanced-label-formats:
-
-Label Formats on the Client Side
---------------------------------------
-
-The lookup label is the text which is shown in the list before it is selected.
-You can use the :ref:`get_item_label <lookup-get-item-label>` method in your lookup
-to do this on the server side. This works for most applications. However if you don't
-want to write your HTML in Python or need to adapt the format on the client side you
-can use the :ref:`formatLabel <javascript-formatLabel>` option.
-
-``formatLabel`` takes two paramaters the current label and the current selected item.
-The item is a dictionary object matching what is returned by the lookup's
-:ref:`format_item <lookup-format-item>`. ``formatLabel`` should return the string
-which should be used for the label.
-
-Going back to the ``CityLookup`` we can adjust the label to wrap the city and state
-portions with their own classes for additional styling:
-
-    .. code-block:: html
-
-        <script type="text/javascript">
-            $(document).ready(function() {
-                function formatLabel(label, item) {
-                    var data = label.split(',');
-                    return '<span class="city">' + data[0] + '</span>, <span class="state">' + data[1] + '</span>';
-                }
-                $('#id_city_0').djselectable('option', 'formatLabel', formatLabel);
-            });
-        </script>
-
-This is a rather simple example but you could also pass additional information in ``format_item``
-such as a flag of whether the city is the capital and render the state captials differently.
-
-.. _advanced-bootstrap:
-
-Using with Twitter Bootstrap
---------------------------------------
-
-django-selectable can work along side with Twitter Bootstrap but there are a few things to
-take into consideration. Both jQuery UI and Bootstrap define a ``$.button`` plugin. This
-plugin is used by default by django-selectable and expects the UI version. If the jQuery UI
-JS is included after the Bootstrap JS then this will work just fine but the Bootstrap
-button JS will not be available. This is the strategy taken by the  `jQuery UI Bootstrap
-<http://addyosmani.github.com/jquery-ui-bootstrap/>`_ theme.
-
-Another option is to rename the Bootstrap plugin using the ``noConflict`` option.
-
-    .. code-block:: html
-
-        <!-- Include Bootstrap JS -->
-        <script>$.fn.bootstrapBtn = $.fn.button.noConflict();</script>
-        <!-- Include jQuery UI JS -->
-
-Even with this some might complain that it's too resource heavy to include all of
-jQuery UI when you just want the autocomplete to work with django-selectable. For
-this you can use the `Download Builder <http://jqueryui.com/download/>`_ to build
-a minimal set of jQuery UI widgets. django-selectable requires the UI core, autocomplete,
-menu and button widgets. None of the effects or interactions are needed. Minified
-this totals around 100 kb of JS, CSS and images (based on jQuery UI 1.10).
-
-.. note::
-
-    For a comparison this is smaller than the minified Bootstrap 2.3.0 CSS
-    which is 105 kb not including the responsive CSS or the icon graphics.
-
-It is possible to remove the dependency on the UI button plugin and instead
-use the Bootstrap button styles. This is done by overriding
-the ``_comboButtonTemplate`` and ``_removeButtonTemplate`` functions used to
-create the buttons. An example is given below.
-
-    .. code-block:: html
-
-        <script>
-            $.ui.djselectable.prototype._comboButtonTemplate = function (input) {
-                var icon = $("<i>").addClass("icon-chevron-down");
-                // Remove current classes on the text input
-                $(input).attr("class", "");
-                // Wrap with input-append
-                $(input).wrap('<div class="input-append" />');
-                // Return button link with the chosen icon
-                return $("<a>").append(icon).addClass("btn btn-small");
-            };
-            $.ui.djselectable.prototype._removeButtonTemplate = function (item) {
-                var icon = $("<i>").addClass("icon-remove-sign");
-                // Return button link with the chosen icon
-                return $("<a>").append(icon).addClass("btn btn-small pull-right");
-            };
-        </script>
diff --git a/dep/django-selectable/docs/conf.py b/dep/django-selectable/docs/conf.py
deleted file mode 100644 (file)
index 7343849..0000000
+++ /dev/null
@@ -1,218 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Django-Selectable documentation build configuration file, created by
-# sphinx-quickstart on Sat Mar 12 14:14:16 2011.
-#
-# This file is execfile()d with the current directory set to its containing dir.
-#
-# Note that not all possible configuration values are present in this
-# autogenerated file.
-#
-# All configuration values have a default; values that are commented out
-# serve to show the default.
-
-import datetime
-import sys, os
-import selectable
-
-# If extensions (or modules to document with autodoc) are in another directory,
-# add these directories to sys.path here. If the directory is relative to the
-# documentation root, use os.path.abspath to make it absolute, like shown here.
-#sys.path.insert(0, os.path.abspath('.'))
-
-# -- General configuration -----------------------------------------------------
-
-# If your documentation needs a minimal Sphinx version, state it here.
-#needs_sphinx = '1.0'
-
-# Add any Sphinx extension module names here, as strings. They can be extensions
-# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = []
-
-# Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
-
-# The suffix of source filenames.
-source_suffix = '.rst'
-
-# The encoding of source files.
-#source_encoding = 'utf-8-sig'
-
-# The master toctree document.
-master_doc = 'index'
-
-# General information about the project.
-project = u'Django-Selectable'
-copyright = u'2011-%s, Mark Lavin' % datetime.date.today().year
-
-# The version info for the project you're documenting, acts as replacement for
-# |version| and |release|, also used in various other places throughout the
-# built documents.
-#
-# The short X.Y version.
-version = '.'.join(selectable.__version__.split('.')[0:2])
-# The full version, including alpha/beta/rc tags.
-release = selectable.__version__
-
-# The language for content autogenerated by Sphinx. Refer to documentation
-# for a list of supported languages.
-#language = None
-
-# There are two options for replacing |today|: either, you set today to some
-# non-false value, then it is used:
-#today = ''
-# Else, today_fmt is used as the format for a strftime call.
-#today_fmt = '%B %d, %Y'
-
-# List of patterns, relative to source directory, that match files and
-# directories to ignore when looking for source files.
-exclude_patterns = ['_build']
-
-# The reST default role (used for this markup: `text`) to use for all documents.
-#default_role = None
-
-# If true, '()' will be appended to :func: etc. cross-reference text.
-#add_function_parentheses = True
-
-# If true, the current module name will be prepended to all description
-# unit titles (such as .. function::).
-#add_module_names = True
-
-# If true, sectionauthor and moduleauthor directives will be shown in the
-# output. They are ignored by default.
-#show_authors = False
-
-# The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
-
-# A list of ignored prefixes for module index sorting.
-#modindex_common_prefix = []
-
-
-# -- Options for HTML output ---------------------------------------------------
-
-# The theme to use for HTML and HTML Help pages.  See the documentation for
-# a list of builtin themes.
-html_theme = 'default'
-
-# Theme options are theme-specific and customize the look and feel of a theme
-# further.  For a list of options available for each theme, see the
-# documentation.
-#html_theme_options = {}
-
-# Add any paths that contain custom themes here, relative to this directory.
-#html_theme_path = []
-
-# The name for this set of Sphinx documents.  If None, it defaults to
-# "<project> v<release> documentation".
-#html_title = None
-
-# A shorter title for the navigation bar.  Default is the same as html_title.
-#html_short_title = None
-
-# The name of an image file (relative to this directory) to place at the top
-# of the sidebar.
-#html_logo = None
-
-# The name of an image file (within the static path) to use as favicon of the
-# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
-# pixels large.
-#html_favicon = None
-
-# Add any paths that contain custom static files (such as style sheets) here,
-# relative to this directory. They are copied after the builtin static files,
-# so a file named "default.css" will overwrite the builtin "default.css".
-#html_static_path = ['_static']
-
-# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
-# using the given strftime format.
-#html_last_updated_fmt = '%b %d, %Y'
-
-# If true, SmartyPants will be used to convert quotes and dashes to
-# typographically correct entities.
-#html_use_smartypants = True
-
-# Custom sidebar templates, maps document names to template names.
-#html_sidebars = {}
-
-# Additional templates that should be rendered to pages, maps page names to
-# template names.
-#html_additional_pages = {}
-
-# If false, no module index is generated.
-#html_domain_indices = True
-
-# If false, no index is generated.
-#html_use_index = True
-
-# If true, the index is split into individual pages for each letter.
-#html_split_index = False
-
-# If true, links to the reST sources are added to the pages.
-#html_show_sourcelink = True
-
-# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
-#html_show_sphinx = True
-
-# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
-#html_show_copyright = True
-
-# If true, an OpenSearch description file will be output, and all pages will
-# contain a <link> tag referring to it.  The value of this option must be the
-# base URL from which the finished HTML is served.
-#html_use_opensearch = ''
-
-# This is the file name suffix for HTML files (e.g. ".xhtml").
-#html_file_suffix = None
-
-# Output file base name for HTML help builder.
-htmlhelp_basename = 'Django-Selectabledoc'
-
-
-# -- Options for LaTeX output --------------------------------------------------
-
-# The paper size ('letter' or 'a4').
-#latex_paper_size = 'letter'
-
-# The font size ('10pt', '11pt' or '12pt').
-#latex_font_size = '10pt'
-
-# Grouping the document tree into LaTeX files. List of tuples
-# (source start file, target name, title, author, documentclass [howto/manual]).
-latex_documents = [
-  ('index', 'Django-Selectable.tex', u'Django-Selectable Documentation',
-   u'Mark Lavin', 'manual'),
-]
-
-# The name of an image file (relative to this directory) to place at the top of
-# the title page.
-#latex_logo = None
-
-# For "manual" documents, if this is true, then toplevel headings are parts,
-# not chapters.
-#latex_use_parts = False
-
-# If true, show page references after internal links.
-#latex_show_pagerefs = False
-
-# If true, show URL addresses after external links.
-#latex_show_urls = False
-
-# Additional stuff for the LaTeX preamble.
-#latex_preamble = ''
-
-# Documents to append as an appendix to all manuals.
-#latex_appendices = []
-
-# If false, no module index is generated.
-#latex_domain_indices = True
-
-
-# -- Options for manual page output --------------------------------------------
-
-# One entry per manual page. List of tuples
-# (source start file, name, description, authors, manual section).
-man_pages = [
-    ('index', 'django-selectable', u'Django-Selectable Documentation',
-     [u'Mark Lavin'], 1)
-]
diff --git a/dep/django-selectable/docs/contribute.rst b/dep/django-selectable/docs/contribute.rst
deleted file mode 100644 (file)
index 67bdba5..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-.. _contributing-guide:
-
-Contributing
-==================
-
-There are plenty of ways to contribute to this project. If you think you've found
-a bug please submit an issue. If there is a feature you'd like to see then please
-open an ticket proposal for it. If you've come up with some helpful examples then
-you can add to our example project.
-
-
-Getting the Source
---------------------------------------
-
-The source code is hosted on `Github <https://github.com/mlavin/django-selectable>`_.
-You can download the full source by cloning the git repo::
-
-    git clone git://github.com/mlavin/django-selectable.git
-
-Feel free to fork the project and make your own changes. If you think that it would
-be helpful for other then please submit a pull request to have it merged in.
-
-
-Submit an Issue
---------------------------------------
-
-The issues are also managed on `Github issue page <https://github.com/mlavin/django-selectable/issues>`_.
-If you think you've found a bug it's helpful if you indicate the version of django-selectable
-you are using the ticket version flag. If you think your bug is javascript related it is
-also helpful to know the version of jQuery, jQuery UI, and the browser you are using.
-
-Issues are also used to track new features. If you have a feature you would like to see
-you can submit a proposal ticket. You can also see features which are planned here.
-
-
-Submit a Translation
---------------------------------------
-
-We are working towards translating django-selectable into different languages. There
-are not many strings to be translated so it is a reasonably easy task and a great way
-to be involved with the project. The translations are managed through
-`Transifex <https://www.transifex.com/projects/p/django-selectable/>`_.
-
-Running the Test Suite
---------------------------------------
-
-There are a number of tests in place to test the server side code for this
-project. To run the tests you need Django and `mock <http://www.voidspace.org.uk/python/mock/>`_
-installed and run::
-
-    python runtests.py
-
-`tox <http://tox.readthedocs.org/en/latest/index.html>`_ is used to test django-selectable
-against multiple versions of Django/Python. With tox installed you can run::
-
-    tox
-
-to run all the version combinations. You can also run tox against a subset of supported
-environments::
-
-    tox -e py27-django15
-
-For more information on running/installing tox please see the
-tox documentation: http://tox.readthedocs.org/en/latest/index.html
-
-Client side tests are written using `QUnit <http://docs.jquery.com/QUnit>`_. They
-can be found in ``selectable/tests/qunit/index.html``. The test suite also uses
-`PhantomJS <http://phantomjs.org/>`_ to
-run the tests. You can install PhantomJS from NPM::
-
-    # Install requirements
-    npm install -g phantomjs jshint
-    make test-js
-
-
-Building the Documentation
---------------------------------------
-
-The documentation is built using `Sphinx <http://sphinx.pocoo.org/>`_
-and available on `Read the Docs <http://django-selectable.readthedocs.io/>`_. With
-Sphinx installed you can build the documentation by running::
-
-    make html
-
-inside the docs directory. Documentation fixes and improvements are always welcome.
-
diff --git a/dep/django-selectable/docs/fields.rst b/dep/django-selectable/docs/fields.rst
deleted file mode 100644 (file)
index 60cae8f..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-Fields
-==========
-
-Django-Selectable defines a number of fields for selecting either single or multiple
-lookup items. Item in this context corresponds to the object return by the underlying
-lookup ``get_item``. The single select select field :ref:`AutoCompleteSelectField`
-allows for the creation of new items. To use this feature the field's
-lookup class must define ``create_item``. In the case of lookups extending from
-:ref:`ModelLookup` newly created items have not yet been saved into the database and saving
-should be handled by the form. All fields take the lookup class as the first required
-argument.
-
-
-.. _AutoCompleteSelectField:
-
-AutoCompleteSelectField
---------------------------------------
-
-Field tied to :ref:`AutoCompleteSelectWidget` to bind the selection to the form and
-create new items, if allowed. The ``allow_new`` keyword argument (default: ``False``)
-which determines if the field allows new items. This field cleans to a single item.
-
-    .. code-block:: python
-
-        from django import forms
-
-        from selectable.forms import AutoCompleteSelectField
-
-        from .lookups import FruitLookup
-
-
-        class FruitSelectionForm(forms.Form):
-            fruit = AutoCompleteSelectField(lookup_class=FruitLookup, label='Select a fruit')
-
-`lookup_class`` may also be a dotted path.
-
-
-.. _AutoCompleteSelectMultipleField:
-
-AutoCompleteSelectMultipleField
---------------------------------------
-
-Field tied to :ref:`AutoCompleteSelectMultipleWidget` to bind the selection to the form.
-This field cleans to a list of items. :ref:`AutoCompleteSelectMultipleField` does not
-allow for the creation of new items.
-
-
-    .. code-block:: python
-
-        from django import forms
-
-        from selectable.forms import AutoCompleteSelectMultipleField
-
-        from .lookups import FruitLookup
-
-
-        class FruitsSelectionForm(forms.Form):
-            fruits = AutoCompleteSelectMultipleField(lookup_class=FruitLookup,
-                label='Select your favorite fruits')
diff --git a/dep/django-selectable/docs/index.rst b/dep/django-selectable/docs/index.rst
deleted file mode 100644 (file)
index 6570310..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-.. include:: ../README.rst
-
-Contents:
-
-.. toctree::
-    :maxdepth: 2
-
-    overview
-    quick-start
-    lookups
-    advanced
-    admin
-    testing
-    fields
-    widgets
-    settings
-    contribute
-    releases
-
-
-Indices and tables
-==================
-
-* :ref:`genindex`
-* :ref:`modindex`
-* :ref:`search`
-
diff --git a/dep/django-selectable/docs/lookups.rst b/dep/django-selectable/docs/lookups.rst
deleted file mode 100644 (file)
index 00edbae..0000000
+++ /dev/null
@@ -1,297 +0,0 @@
-Defining Lookups
-==================
-
-What are Lookups?
---------------------------------------
-
-Lookups define the corresponding ajax views used by the auto-completion
-fields and widgets. They take in the current request and return the JSON
-needed by the jQuery auto-complete plugin.
-
-
-Defining a Lookup
---------------------------------------
-
-django-selectable uses a registration pattern similar to the Django admin.
-Lookups should be defined in a `lookups.py` in your application's module. Once defined
-you must register in with django-selectable. All lookups must extend from
-``selectable.base.LookupBase`` which defines the API for every lookup.
-
-    .. code-block:: python
-
-        from selectable.base import LookupBase
-        from selectable.registry import registry
-
-        class MyLookup(LookupBase):
-            def get_query(self, request, term):
-                data = ['Foo', 'Bar']
-                return [x for x in data if x.startswith(term)]
-
-        registry.register(MyLookup)
-
-
-Lookup API
---------------------------------------
-
-.. py:method:: LookupBase.get_query(request, term)
-
-    This is the main method which takes the current request
-    from the user and returns the data which matches their search.
-
-    :param request: The current request object.
-    :param term: The search term from the widget input.
-    :return: An iterable set of data of items matching the search term.
-
-.. _lookup-get-item-label:
-
-.. py:method:: LookupBase.get_item_label(item)
-
-    This is first of three formatting methods. The label is shown in the
-    drop down menu of search results. This defaults to ``item.__unicode__``.
-
-    :param item: An item from the search results.
-    :return: A string representation of the item to be shown in the search results.
-        The label can include HTML. For changing the label format on the client side
-        see :ref:`Advanced Label Formats <advanced-label-formats>`.
-
-
-.. py:method:: LookupBase.get_item_id(item)
-
-    This is second of three formatting methods. The id is the value that will eventually
-    be returned by the field/widget. This defaults to ``item.__unicode__``.
-
-    :param item: An item from the search results.
-    :return: A string representation of the item to be returned by the field/widget.
-
-
-.. py:method:: LookupBase.split_term(term)
-
-     Split searching term into array of subterms that will be searched separately.
-     You can override this function to achieve different splitting of the term.
-
-    :param term: The search term.
-    :return: Array with subterms
-
-.. py:method:: LookupBase.get_item_value(item)
-
-    This is last of three formatting methods. The value is shown in the
-    input once the item has been selected. This defaults to ``item.__unicode__``.
-
-    :param item: An item from the search results.
-    :return: A string representation of the item to be shown in the input.
-
-.. py:method:: LookupBase.get_item(value)
-
-    ``get_item`` is the reverse of ``get_item_id``. This should take the value
-    from the form initial values and return the current item. This defaults
-    to simply return the value.
-
-    :param value: Value from the form inital value.
-    :return: The item corresponding to the initial value.
-
-.. py:method:: LookupBase.create_item(value)
-
-    If you plan to use a lookup with a field or widget which allows the user
-    to input new values then you must define what it means to create a new item
-    for your lookup. By default this raises a ``NotImplemented`` error.
-
-    :param value: The user given value.
-    :return: The new item created from the item.
-
-.. _lookup-format-item:
-
-.. py:method:: LookupBase.format_item(item)
-
-    By default ``format_item`` creates a dictionary with the three keys used by
-    the UI plugin: id, value, label. These are generated from the calls to
-    ``get_item_id``, ``get_item_value`` and ``get_item_label``. If you want to
-    add additional keys you should add them here.
-
-    The results of ``get_item_label`` is conditionally escaped to prevent
-    Cross Site Scripting (XSS) similar to the templating language.
-    If you know that the content is safe and you want to use these methods
-    to include HTML should mark the content as safe with ``django.utils.safestring.mark_safe``
-    inside the ``get_item_label`` method.
-
-    ``get_item_id`` and ``get_item_value`` are not escapted by default. These are
-    not a XSS vector with the built-in JS. If you are doing additional formating using
-    these values you should be conscience of this fake and be sure to escape these
-    values.
-
-    :param item: An item from the search results.
-    :return: A dictionary of information for this item to be sent back to the client.
-
-There are also some additional methods that you could want to use/override. These
-are for more advanced use cases such as using the lookups with JS libraries other
-than jQuery UI. Most users will not need to override these methods.
-
-.. _lookup-format-results:
-
-.. py:method:: LookupBase.format_results(self, raw_data, options)
-
-    Returns a python structure that later gets serialized. This makes a call to
-    :ref:`paginate_results<lookup-paginate-results>` prior to calling
-    :ref:`format_item<lookup-format-item>` on each item in the current page.
-
-    :param raw_data: The set of all matched results.
-    :param options: Dictionary of ``cleaned_data`` from the lookup form class.
-    :return: A dictionary with two keys ``meta`` and ``data``.
-        The value of ``data`` is an iterable extracted from page_data.
-        The value of ``meta`` is a dictionary. This is a copy of options with one additional element
-        ``more`` which is a translatable "Show more" string
-        (useful for indicating more results on the javascript side).
-
-.. _lookup-paginate-results:
-
-.. py:method:: LookupBase.paginate_results(results, options)
-
-    If :ref:`SELECTABLE_MAX_LIMIT` is defined or ``limit`` is passed in request.GET
-    then ``paginate_results`` will return the current page using Django's
-    built in pagination. See the Django docs on
-    `pagination <https://docs.djangoproject.com/en/stable/topics/pagination/>`_
-    for more info.
-
-    :param results: The set of all matched results.
-    :param options: Dictionary of ``cleaned_data`` from the lookup form class.
-    :return: The current `Page object <https://docs.djangoproject.com/en/stable/topics/pagination/#page-objects>`_
-        of results.
-
-
-.. _ModelLookup:
-
-Lookups Based on Models
---------------------------------------
-
-Perhaps the most common use case is to define a lookup based on a given Django model.
-For this you can extend ``selectable.base.ModelLookup``. To extend ``ModelLookup`` you
-should set two class attributes: ``model`` and ``search_fields``.
-
-    .. code-block:: python
-
-        from __future__ import unicode_literals
-
-        from selectable.base import ModelLookup
-        from selectable.registry import registry
-
-        from .models import Fruit
-
-
-        class FruitLookup(ModelLookup):
-            model = Fruit
-            search_fields = ('name__icontains', )
-
-        registry.register(FruitLookup)
-
-The syntax for ``search_fields`` is the same as the Django
-`field lookup syntax <http://docs.djangoproject.com/en/stable/ref/models/querysets/#field-lookups>`_.
-Each of these lookups are combined as OR so any one of them matching will return a
-result. You may optionally define a third class attribute ``filters`` which is a dictionary of
-filters to be applied to the model queryset. The keys should be a string defining a field lookup
-and the value should be the value for the field lookup. Filters on the other hand are
-combined with AND.
-
-
-User Lookup Example
---------------------------------------
-
-Below is a larger model lookup example using multiple search fields, filters
-and display options for the `auth.User <https://docs.djangoproject.com/en/stable/topics/auth/#users>`_
-model.
-
-    .. code-block:: python
-
-        from django.contrib.auth.models import User
-        from selectable.base import ModelLookup
-        from selectable.registry import registry
-
-
-        class UserLookup(ModelLookup):
-            model = User
-            search_fields = (
-                'username__icontains',
-                'first_name__icontains',
-                'last_name__icontains',
-            )
-            filters = {'is_active': True, }
-
-            def get_item_value(self, item):
-                # Display for currently selected item
-                return item.username
-
-            def get_item_label(self, item):
-                # Display for choice listings
-                return u"%s (%s)" % (item.username, item.get_full_name())
-
-        registry.register(UserLookup)
-
-
-.. _lookup-decorators:
-
-Lookup Decorators
---------------------------------------
-
-Registering lookups with django-selectable creates a small API for searching the
-lookup data. While the amount of visible data is small there are times when you want
-to restrict the set of requests which can view the data. For this purpose there are
-lookup decorators. To use them you simply decorate your lookup class.
-
-    .. code-block:: python
-
-        from django.contrib.auth.models import User
-        from selectable.base import ModelLookup
-        from selectable.decorators import login_required
-        from selectable.registry import registry
-
-
-        @login_required
-        class UserLookup(ModelLookup):
-            model = User
-            search_fields = ('username__icontains', )
-            filters = {'is_active': True, }
-
-        registry.register(UserLookup)
-
-.. note::
-
-    The class decorator syntax was introduced in Python 2.6. If you are using
-    django-selectable with Python 2.5 you can still make use of these decorators
-    by applying the without the decorator syntax.
-
-    .. code-block:: python
-
-        class UserLookup(ModelLookup):
-            model = User
-            search_fields = ('username__icontains', )
-            filters = {'is_active': True, }
-
-        UserLookup = login_required(UserLookup)
-
-        registry.register(UserLookup)
-
-Below are the descriptions of the available lookup decorators.
-
-
-ajax_required
-______________________________________
-
-The django-selectable javascript will always request the lookup data via
-XMLHttpRequest (AJAX) request. This decorator enforces that the lookup can only
-be accessed in this way. If the request is not an AJAX request then it will return
-a 400 Bad Request response.
-
-
-login_required
-______________________________________
-
-This decorator requires the user to be authenticated via ``request.user.is_authenticated``.
-If the user is not authenticated this will return a 401 Unauthorized response.
-``request.user`` is set by the ``django.contrib.auth.middleware.AuthenticationMiddleware``
-which is required for this decorator to work. This middleware is enabled by default.
-
-staff_member_required
-______________________________________
-
-This decorator builds from ``login_required`` and in addition requires that
-``request.user.is_staff`` is ``True``. If the user is not authenticatated this will
-continue to return at 401 response. If the user is authenticated but not a staff member
-then this will return a 403 Forbidden response.
diff --git a/dep/django-selectable/docs/make.bat b/dep/django-selectable/docs/make.bat
deleted file mode 100644 (file)
index 253570f..0000000
+++ /dev/null
@@ -1,170 +0,0 @@
-@ECHO OFF
-
-REM Command file for Sphinx documentation
-
-if "%SPHINXBUILD%" == "" (
-       set SPHINXBUILD=sphinx-build
-)
-set BUILDDIR=_build
-set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
-if NOT "%PAPER%" == "" (
-       set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
-)
-
-if "%1" == "" goto help
-
-if "%1" == "help" (
-       :help
-       echo.Please use `make ^<target^>` where ^<target^> is one of
-       echo.  html       to make standalone HTML files
-       echo.  dirhtml    to make HTML files named index.html in directories
-       echo.  singlehtml to make a single large HTML file
-       echo.  pickle     to make pickle files
-       echo.  json       to make JSON files
-       echo.  htmlhelp   to make HTML files and a HTML help project
-       echo.  qthelp     to make HTML files and a qthelp project
-       echo.  devhelp    to make HTML files and a Devhelp project
-       echo.  epub       to make an epub
-       echo.  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter
-       echo.  text       to make text files
-       echo.  man        to make manual pages
-       echo.  changes    to make an overview over all changed/added/deprecated items
-       echo.  linkcheck  to check all external links for integrity
-       echo.  doctest    to run all doctests embedded in the documentation if enabled
-       goto end
-)
-
-if "%1" == "clean" (
-       for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
-       del /q /s %BUILDDIR%\*
-       goto end
-)
-
-if "%1" == "html" (
-       %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
-       if errorlevel 1 exit /b 1
-       echo.
-       echo.Build finished. The HTML pages are in %BUILDDIR%/html.
-       goto end
-)
-
-if "%1" == "dirhtml" (
-       %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
-       if errorlevel 1 exit /b 1
-       echo.
-       echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
-       goto end
-)
-
-if "%1" == "singlehtml" (
-       %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
-       if errorlevel 1 exit /b 1
-       echo.
-       echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
-       goto end
-)
-
-if "%1" == "pickle" (
-       %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
-       if errorlevel 1 exit /b 1
-       echo.
-       echo.Build finished; now you can process the pickle files.
-       goto end
-)
-
-if "%1" == "json" (
-       %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
-       if errorlevel 1 exit /b 1
-       echo.
-       echo.Build finished; now you can process the JSON files.
-       goto end
-)
-
-if "%1" == "htmlhelp" (
-       %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
-       if errorlevel 1 exit /b 1
-       echo.
-       echo.Build finished; now you can run HTML Help Workshop with the ^
-.hhp project file in %BUILDDIR%/htmlhelp.
-       goto end
-)
-
-if "%1" == "qthelp" (
-       %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
-       if errorlevel 1 exit /b 1
-       echo.
-       echo.Build finished; now you can run "qcollectiongenerator" with the ^
-.qhcp project file in %BUILDDIR%/qthelp, like this:
-       echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Django-Selectable.qhcp
-       echo.To view the help file:
-       echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Django-Selectable.ghc
-       goto end
-)
-
-if "%1" == "devhelp" (
-       %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
-       if errorlevel 1 exit /b 1
-       echo.
-       echo.Build finished.
-       goto end
-)
-
-if "%1" == "epub" (
-       %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
-       if errorlevel 1 exit /b 1
-       echo.
-       echo.Build finished. The epub file is in %BUILDDIR%/epub.
-       goto end
-)
-
-if "%1" == "latex" (
-       %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
-       if errorlevel 1 exit /b 1
-       echo.
-       echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
-       goto end
-)
-
-if "%1" == "text" (
-       %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
-       if errorlevel 1 exit /b 1
-       echo.
-       echo.Build finished. The text files are in %BUILDDIR%/text.
-       goto end
-)
-
-if "%1" == "man" (
-       %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
-       if errorlevel 1 exit /b 1
-       echo.
-       echo.Build finished. The manual pages are in %BUILDDIR%/man.
-       goto end
-)
-
-if "%1" == "changes" (
-       %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
-       if errorlevel 1 exit /b 1
-       echo.
-       echo.The overview file is in %BUILDDIR%/changes.
-       goto end
-)
-
-if "%1" == "linkcheck" (
-       %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
-       if errorlevel 1 exit /b 1
-       echo.
-       echo.Link check complete; look for any errors in the above output ^
-or in %BUILDDIR%/linkcheck/output.txt.
-       goto end
-)
-
-if "%1" == "doctest" (
-       %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
-       if errorlevel 1 exit /b 1
-       echo.
-       echo.Testing of doctests in the sources finished, look at the ^
-results in %BUILDDIR%/doctest/output.txt.
-       goto end
-)
-
-:end
diff --git a/dep/django-selectable/docs/overview.rst b/dep/django-selectable/docs/overview.rst
deleted file mode 100644 (file)
index 6a097a6..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-Overview
-==================
-
-Motivation
---------------------------------------
-
-There are many Django apps related to auto-completion why create another? One problem
-was varying support for the `jQuery UI auto-complete plugin <http://jqueryui.com/demos/autocomplete/>`_ 
-versus the now deprecated `bassistance version <http://bassistance.de/jquery-plugins/jquery-plugin-autocomplete/>`_.
-Another was support for combo-boxes and multiple selects. And lastly was a simple syntax for
-defining the related backend views for the auto-completion.
-
-This library aims to meet all of these goals:
-    - Built on jQuery UI auto-complete
-    - Fields and widgets for a variety of use-cases:
-        - Text inputs and combo-boxes
-        - Text selection
-        - Value/ID/Foreign key selection
-        - Multiple object selection
-        - Allowing new values
-    - Simple and extendable syntax for defining backend views
-
-
-Related Projects
---------------------------------------
-
-Much of the work here was inspired by things that I like (and things I don't like) about
-`django-ajax-selects <http://code.google.com/p/django-ajax-selects/>`_. To see some of the
-other Django apps for handling auto-completion see `Django-Packages <http://djangopackages.com/grids/g/auto-complete/>`_.
diff --git a/dep/django-selectable/docs/quick-start.rst b/dep/django-selectable/docs/quick-start.rst
deleted file mode 100644 (file)
index d5a570d..0000000
+++ /dev/null
@@ -1,178 +0,0 @@
-Getting Started
-==================
-
-The workflow for using `django-selectable` involves two main parts:
-    - Defining your lookups
-    - Defining your forms
-
-This guide assumes that you have a basic knowledge of creating Django models and
-forms. If not you should first read through the documentation on
-`defining models <http://docs.djangoproject.com/en/stable/topics/db/models/>`_
-and `using forms <http://docs.djangoproject.com/en/stable/topics/forms/>`_.
-
-.. _start-include-jquery:
-
-Including jQuery & jQuery UI
---------------------------------------
-
-The widgets in django-selectable define the media they need as described in the
-Django documentation on `Form Media <https://docs.djangoproject.com/en/stable/topics/forms/media/>`_.
-That means to include the javascript and css you need to make the widgets work you
-can include ``{{ form.media.css }}`` and ``{{ form.media.js }}`` in your template. This is
-assuming your form is called `form` in the template context. For more information
-please check out the `Django documentation <https://docs.djangoproject.com/en/stable/topics/forms/media/>`_.
-
-The jQuery and jQuery UI libraries are not included in the distribution but must be included
-in your templates. However there is a template tag to easily add these libraries from
-the  from the `Google CDN <http://code.google.com/apis/libraries/devguide.html#jquery>`_.
-
-    .. code-block:: html
-
-        {% load selectable_tags %}
-        {% include_jquery_libs %}
-
-By default these will use jQuery v1.11.2 and jQuery UI v1.11.3. You can customize the versions
-used by pass them to the tag. The first version is the jQuery version and the second is the
-jQuery UI version.
-
-    .. code-block:: html
-
-        {% load selectable_tags %}
-        {% include_jquery_libs '1.11.2' '1.11.3' %}
-
-Django-Selectable should work with `jQuery <http://jquery.com/>`_ >= 1.9 and
-`jQuery UI <http://jqueryui.com/>`_ >= 1.10.
-
-You must also include a `jQuery UI theme <http://jqueryui.com/themeroller/>`_ stylesheet. There
-is also a template tag to easily add this style sheet from the Google CDN.
-
-    .. code-block:: html
-
-        {% load selectable_tags %}
-        {% include_ui_theme %}
-
-By default this will use the `base <http://jqueryui.com/themeroller/>`_ theme for jQuery UI v1.11.4.
-You can configure the theme and version by passing them in the tag.
-
-    .. code-block:: html
-
-        {% load selectable_tags %}
-        {% include_ui_theme 'ui-lightness' '1.11.4' %}
-
-Or only change the theme.
-
-    .. code-block:: html
-
-        {% load selectable_tags %}
-        {% include_ui_theme 'ui-lightness' %}
-
-See the the jQuery UI documentation for a full list of available stable themes: http://jqueryui.com/download#stable-themes
-
-Of course you can choose to include these rescources manually::
-
-    .. code-block:: html
-
-        <link rel="stylesheet" href="//ajax.googleapis.com/ajax/libs/jqueryui/1.11.3/themes/base/jquery-ui.css" type="text/css">
-        <link href="{% static 'selectable/css/dj.selectable.css' %}" type="text/css" media="all" rel="stylesheet">
-        <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
-        <script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.11.3/jquery-ui.js"></script>
-        <script type="text/javascript" src="{% static 'selectable/js/jquery.dj.selectable.js' %}"></script>
-
-.. note::
-
-    jQuery UI shares a few plugin names with the popular Twitter Bootstrap framework. There
-    are notes on using Bootstrap along with django-selectable in the :ref:`advanced usage
-    section <advanced-bootstrap>`.
-
-
-Defining a Lookup
---------------------------------
-
-The lookup classes define the backend views. The most common case is defining a
-lookup which searchs models based on a particular field. Let's define a simple model:
-
-    .. code-block:: python
-
-        from __future__ import unicode_literals
-
-        from django.db import models
-        from django.utils.encoding import python_2_unicode_compatible
-
-
-        @python_2_unicode_compatible
-        class Fruit(models.Model):
-            name = models.CharField(max_length=200)
-
-            def __str__(self):
-                return self.name
-
-In a `lookups.py` we will define our lookup:
-
-    .. code-block:: python
-
-        from __future__ import unicode_literals
-
-        from selectable.base import ModelLookup
-        from selectable.registry import registry
-
-        from .models import Fruit
-
-
-        class FruitLookup(ModelLookup):
-            model = Fruit
-            search_fields = ('name__icontains', )
-
-
-This lookups extends ``selectable.base.ModelLookup`` and defines two things: one is
-the model on which we will be searching and the other is the field which we are searching.
-This syntax should look familiar as it is the same as the `field lookup syntax <http://docs.djangoproject.com/en/stable/ref/models/querysets/#field-lookups>`_
-for making queries in Django.
-
-Below this definition we will register our lookup class.
-
-    .. code-block:: python
-
-        registry.register(FruitLookup)
-
-.. note::
-
-    You should only register your lookup once. Attempting to register the same lookup class
-    more than once will lead to ``LookupAlreadyRegistered`` errors. A common problem related to the
-    ``LookupAlreadyRegistered`` error is related to inconsistant import paths in your project.
-    Prior to Django 1.4 the default ``manage.py`` allows for importing both with and without
-    the project name (i.e. ``from myproject.myapp import lookups`` or ``from myapp import lookups``).
-    This leads to the ``lookup.py`` file being imported twice and the registration code
-    executing twice. Thankfully this is no longer the default in Django 1.4. Keeping
-    your import consistant to include the project name (when your app is included inside the
-    project directory) will avoid these errors.
-
-
-Defining Forms
---------------------------------
-
-Now that we have a working lookup we will define a form which uses it:
-
-    .. code-block:: python
-
-        from django import forms
-
-        from selectable.forms import AutoCompleteWidget
-
-        from .lookups import FruitLookup
-
-
-        class FruitForm(forms.Form):
-            autocomplete = forms.CharField(
-                label='Type the name of a fruit (AutoCompleteWidget)',
-                widget=AutoCompleteWidget(FruitLookup),
-                required=False,
-            )
-
-
-This replaces the default widget for the ``CharField`` with the ``AutoCompleteWidget``.
-This will allow the user to fill this field with values taken from the names of
-existing ``Fruit`` models.
-
-And that's pretty much it. Keep on reading if you want to learn about the other
-types of fields and widgets that are available as well as defining more complicated
-lookups.
diff --git a/dep/django-selectable/docs/releases.rst b/dep/django-selectable/docs/releases.rst
deleted file mode 100644 (file)
index c48cedb..0000000
+++ /dev/null
@@ -1,349 +0,0 @@
-Release Notes
-==================
-
-
-v1.2.1 (Released 2019-02-02)
---------------------------------------
-
-Fixed compatibility issue with jQuery UI 1.12. Thanks to Christian Klus (kluchrj) for the fix.
-
-
-v1.2.0 (Released 2018-10-13)
---------------------------------------
-
-Primarily a Django support related release. This version adds support for Django 2.0 and 2.1 while
-dropping support for Django versions below 1.11. A number of deprecation warnings for future Django
-versions have also been addressed.
-
-Added the ability to search on multiple terms split by whitespace.
-
-
-Backwards Incompatible Changes
-________________________________
-
-- Dropped support for Django versions below 1.11
-
-
-v1.1.0 (Released 2018-01-12)
---------------------------------------
-
-- Updated admin docs.
-- Added support for Django 1.11
-
-Special thanks to Luke Plant for contributing the fixes to support Django 1.11.
-
-
-v1.0.0 (Released 2017-04-14)
---------------------------------------
-
-This project has been stable for quite some time and finally declaring a 1.0 release. With
-that comes new policies on official supported versions for Django, Python, jQuery, and jQuery UI.
-
-- New translations for German and Czech.
-- Various bug and compatibility fixes.
-- Updated example project.
-
-Special thanks to Raphael Merx for helping track down issues related to this release
-and an updating the example project to work on Django 1.10.
-
-Backwards Incompatible Changes
-________________________________
-
-- Dropped support Python 2.6 and 3.2
-- Dropped support for Django < 1.7. Django 1.11 is not yet supported.
-- ``LookupBase.serialize_results`` had been removed. This is now handled by the built-in ``JsonResponse`` in Django.
-- jQuery and jQuery UI versions for the ``include_jquery_libs`` and ``include_ui_theme`` template tags have been increased to 1.12.4 and 1.11.4 respectively.
-- Dropped testing support for jQuery < 1.9 and jQuery UI < 1.10. Earlier versions may continue to work but it is recommended to upgrade.
-
-
-v0.9.0 (Released 2014-10-21)
---------------------------------------
-
-This release primarily addresses incompatibility with Django 1.7. The app-loading refactor both
-broke the previous registration and at the same time provided better utilities in Django core to
-make it more robust.
-
-- Compatibility with Django 1.7. Thanks to Calvin Spealman for the fixes.
-- Fixes for Python 3 support.
-
-Backwards Incompatible Changes
-________________________________
-
-- Dropped support for jQuery < 1.7
-
-
-v0.8.0 (Released 2014-01-20)
---------------------------------------
-
-- Widget media references now include a version string for cache-busting when upgrading django-selectable. Thanks to Ustun Ozgur.
-- Added compatibility code for \*SelectWidgets to handle POST data for the default SelectWidget. Thanks to leo-the-manic.
-- Development moved from Bitbucket to Github.
-- Update test suite compatibility with new test runner in Django 1.6. Thanks to Dan Poirier for the report and fix.
-- Tests now run on Travis CI.
-- Added French and Chinese translations.
-
-Backwards Incompatible Changes
-________________________________
-
-- Support for Django < 1.5 has been dropped. Most pieces should continue to work but there was an ugly JS hack to make django-selectable work nicely in the admin which too flakey to continue to maintain. If you aren't using the selectable widgets in inline-forms in the admin you can most likely continue to use Django 1.4 without issue.
-
-
-v0.7.0 (Released 2013-03-01)
---------------------------------------
-
-This release features a large refactor of the JS plugin used by the widgets. While this
-over makes the plugin more maintainable and allowed for some of the new features in this
-release, it does introduce a few incompatible changes. For the most part places where you
-might have previously used the ``autocomplete`` namespace/plugin, those references should
-be updated to reference the ``djselectable`` plugin.
-
-This release also adds experimental support for Python 3.2+ to go along with Django's support in 1.5.
-To use Python 3 with django-selectable you will need to use Django 1.5+.
-
-- Experimental Python 3.2+ support
-- Improved the scope of ``prepareQuery`` and ``formatLabel`` options. Not fully backwards compatible. Thanks to Augusto Men.
-- Allow passing the Python path string in place of the lookup class to the fields and widgets. Thanks to Michael Manfre.
-- Allow passing JS plugin options through the widget ``attrs`` option. Thanks to Felipe Prenholato.
-- Tests for compatibility with jQuery 1.6 through 1.9 and jQuery UI 1.8 through 1.10.
-- Added notes on Bootstrap compatibility.
-- Added compatibility with Grappelli in the admin.
-- Added Spanish translation thanks to Manuel Alvarez.
-- Added documentation notes on testing.
-
-Bug Fixes
-_________________
-
-- Fixed bug with matching hidden input when the name contains '_1'. Thanks to Augusto Men for the report and fix.
-- Fixed bug where the enter button would open the combobox options rather than submit the form. Thanks to Felipe Prenholato for the report.
-- Fixed bug with using ``allow_new=True`` creating items when no data was submitted. See #91.
-- Fixed bug with widget ``has_changed`` when there is no initial data. See #92.
-
-
-Backwards Incompatible Changes
-________________________________
-
-- The JS event namespace has changed from ``autocomplete`` to ``djselectable``.
-- ``data('autocomplete')`` is no longer available on the widgets on the client-side. Use ``data('djselectable')`` instead.
-- Combobox button was changed from a ``<button>`` to ``<a>``. Any customized styles you may have should be updated.
-- Combobox no longer changes the ``minLength`` or ``delay`` options.
-
-
-v0.6.2 (Released 2012-11-07)
---------------------------------------
-
-Bug Fixes
-_________________
-
-- Fixed bug with special characters when highlighting matches. Thanks to Chad Files for the report.
-- Fixed javascript bug with spaces in ``item.id``. Thanks to @dc for the report and fix.
-
-
-v0.6.1 (Released 2012-10-13)
---------------------------------------
-
-Features
-_________________
-
-- Added Polish translation. Thanks to Sławomir Ehlert.
-
-Bug Fixes
-_________________
-
-- Fixed incompatibility with jQuery UI 1.9.
-
-
-v0.6.0 (Released 2012-10-09)
---------------------------------------
-
-This release continues to clean up the API and JS. This was primarily motivated by
-Sławomir Ehlert (@slafs) who is working on an alternate implementation which
-uses Select2 rather than jQuery UI. This opens the door for additional apps
-which use the same lookup declaration API with a different JS library on the front
-end.
-
-Python 2.5 support has been dropped to work towards Python 3 support.
-This also drops Django 1.2 support which is no longer receiving security fixes.
-
-Features
-_________________
-
-- Initial translations (pt_BR). Thanks to Felipe Prenholato for the patch.
-- Upgraded default jQuery UI version included by the template tags from 1.8.18 to 1.8.23
-- Added ``djselectableadd`` and ``djselectableremove`` events fired when items are added or removed from a mutliple select
-
-Bug Fixes
-_________________
-
-- Cleaned up JS scoping problems when multiple jQuery versions are used on the page. Thanks Antti Kaihola for the report.
-- Fixed minor JS bug where text input was not cleared when selected via the combobox in the multiselect. Thanks Antti Kaihola for the report and Lukas Pirl for a hotfix.
-
-Backwards Incompatible Changes
-________________________________
-
-- ``get_item_value`` and ``get_item_id`` are no longer marked as safe by default.
-- Removed AutoComboboxSelectField and AutoComboboxSelectMultipleField. These were deprecated in 0.5.
-- Dropping official Python 2.5 support.
-- Dropping official Django 1.2 support.
-- ``paginate_results`` signature changed as part of the lookup refactor.
-- ``SELECTABLE_MAX_LIMIT`` can no longer be ``None``.
-
-
-v0.5.2 (Released 2012-06-27)
---------------------------------------
-
-Bug Fixes
-_________________
-
-- Fixed XSS flaw with lookup ``get_item_*`` methods. Thanks slafs for the report.
-- Fixed bug when passing widget instance rather than widget class to ``AutoCompleteSelectField`` or ``AutoCompleteSelectMultipleField``.
-
-
-v0.5.1 (Released 2012-06-08)
---------------------------------------
-
-Bug Fixes
-_________________
-
-- Fix for double ``autocompleteselect`` event firing.
-- Fix for broken pagination in search results. Thanks David Ray for report and fix.
-
-
-v0.4.2 (Released 2012-06-08)
---------------------------------------
-
-Bug Fixes
-_________________
-
-- Backported fix for double ``autocompleteselect`` event firing.
-- Backported fix for broken pagination in search results.
-
-
-v0.5.0 (Released 2012-06-02)
---------------------------------------
-
-Features
-_________________
-
-- Template tag to add necessary jQuery and jQuery UI libraries. Thanks to Rick Testore for the initial implementation
-- :ref:`Lookup decorators <lookup-decorators>` for requiring user authentication or staff access to use the lookup
-- Additional documentation
-- Minor updates to the example project
-
-Backwards Incompatible Changes
-________________________________
-
-- Previously the minimal version of jQuery was listed as 1.4.3 when it fact there was a bug a that made django-selectable require 1.4.4. Not a new incompatibility but the docs have now been updated and 1.4.3 compatibility will not be added. Thanks to Rick Testore for the report and the fix
-- Started deprecation path for AutoComboboxSelectField and AutoComboboxSelectMultipleField
-
-
-v0.4.1 (Released 2012-03-11)
---------------------------------------
-
-Bug Fixes
-_________________
-
-- Cleaned up whitespace in css/js. Thanks Dan Poirier for the report and fix.
-- Fixed issue with saving M2M field data with AutoCompleteSelectMultipleField. Thanks Raoul Thill for the report.
-
-
-v0.4.0 (Released 2012-02-25)
---------------------------------------
-
-Features
-_________________
-
-- Better compatibility with :ref:`AutoCompleteSelectWidget`/:ref:`AutoComboboxSelectWidget` and Django's ModelChoiceField
-- Better compatibility with the Django admin :ref:`add another popup <admin-basic-example>`
-- Easier passing of query parameters. See the :ref:`Additional Parameters <additional-parameters>` section
-- Additional documentation
-- QUnit tests for JS functionality
-
-
-Backwards Incompatible Changes
-________________________________
-
-- Support for ``ModelLookup.search_field`` string has been removed. You should use the ``ModelLookup.search_fields`` tuple instead.
-
-
-v0.3.1 (Released 2012-02-23)
---------------------------------------
-
-Bug Fixes
-_________________
-
-- Fixed issue with media urls when not using staticfiles.
-
-
-v0.3.0 (Released 2012-02-15)
---------------------------------------
-
-Features
-_________________
-
-- Multiple search fields for :ref:`model based lookups <ModelLookup>`
-- Support for :ref:`highlighting term matches <javascript-highlightMatch>`
-- Support for HTML in :ref:`result labels <lookup-get-item-label>`
-- Support for :ref:`client side formatting <advanced-label-formats>`
-- Additional documentation
-- Expanded examples in example project
-
-
-Bug Fixes
-_________________
-
-- Fixed issue with Enter key removing items from select multiple widgets `#24 <https://github.com/mlavin/django-selectable/issues/24>`_
-
-
-Backwards Incompatible Changes
-________________________________
-
-- The fix for #24 changed the remove items from a button to an anchor tag. If you were previously using the button tag for additional styling then you will need to adjust your styles.
-- The static resources were moved into a `selectable` sub-directory. This makes the media more in line with the template directory conventions. If you are using the widgets in the admin there is nothing to change. If you are using ``{{ form.media }}`` then there is also nothing to change. However if you were including static media manually then you will need to adjust them to include the selectable prefix.
-
-
-v0.2.0 (Released 2011-08-13)
---------------------------------------
-
-Features
-_________________
-
-- Additional documentation
-- :ref:`Positional configuration <AutoCompleteSelectMultipleWidget>` for multiple select fields/widgets
-- :ref:`Settings/configuration <SELECTABLE_MAX_LIMIT>` for limiting/paginating result sets
-- Compatibility and examples for :ref:`Admin inlines <admin-inline-example>`
-- JS updated for jQuery 1.6 compatibility
-- :ref:`JS hooks <client-side-parameters>` for updating query parameters
-- :ref:`Chained selection example <chain-select-example>`
-
-
-v0.1.2 (Released 2011-05-25)
---------------------------------------
-
-Bug Fixes
-_________________
-
-- Fixed issue `#17 <https://github.com/mlavin/django-selectable/issues/17>`_
-
-
-v0.1.1 (Release 2011-03-21)
---------------------------------------
-
-Bug Fixes
-_________________
-
-- Fixed/cleaned up multiple select fields and widgets
-- Added media definitions to widgets
-
-
-Features
-_________________
-
-- Additional documentation
-- Added `update_query_parameters` to widgets
-- Refactored JS for easier configuration
-
-
-v0.1 (Released 2011-03-13)
---------------------------------------
-
-Initial public release
diff --git a/dep/django-selectable/docs/settings.rst b/dep/django-selectable/docs/settings.rst
deleted file mode 100644 (file)
index 80cf04b..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
-Settings
-==================
-
-
-.. _SELECTABLE_MAX_LIMIT:
-
-SELECTABLE_MAX_LIMIT
---------------------------------------
-
-This setting is used to limit the number of results returned by the auto-complete fields.
-Each field/widget can individually lower this maximum. The result sets will be
-paginated allowing the client to ask for more results. The limit is passed as a
-query parameter and validated against this value to ensure the client cannot manipulate
-the query string to retrive more values.
-
-Default: ``25``
-
-
-.. _SELECTABLE_ESCAPED_KEYS:
-
-SELECTABLE_ESCAPED_KEYS
---------------------------------------
-
-The ``LookupBase.format_item`` will conditionally escape result keys based on this
-setting. The label is escaped by default to prevent a XSS flaw when using the
-jQuery UI autocomplete. If you are using the lookup responses for a different
-autocomplete plugin then you may need to esacpe more keys by default.
-
-Default: ``('label', )``
-
-.. note::
-    You probably don't want to include ``id`` in this setting.
-
-
-.. _javascript-options:
-
-Javascript Plugin Options
---------------------------------------
-
-Below the options for configuring the Javascript behavior of the django-selectable
-widgets.
-
-
-.. _javascript-removeIcon:
-
-removeIcon
-______________________________________
-
-
-This is the class name used for the remove buttons for the multiple select widgets.
-The set of icon classes built into the jQuery UI framework can be found here:
-http://jqueryui.com/themeroller/
-
-Default: ``ui-icon-close``
-
-
-.. _javascript-comboboxIcon:
-
-comboboxIcon
-______________________________________
-
-
-This is the class name used for the combobox dropdown icon. The set of icon classes built 
-into the jQuery UI framework can be found here: http://jqueryui.com/themeroller/
-
-Default: ``ui-icon-triangle-1-s``
-
-
-.. _javascript-prepareQuery:
-
-prepareQuery
-______________________________________
-
-
-``prepareQuery`` is a function that is run prior to sending the search request to
-the server. It is an oppotunity to add additional parameters to the search query.
-It takes one argument which is the current search parameters as a dictionary. For
-more information on its usage see :ref:`Adding Parameters on the Client Side <client-side-parameters>`.
-
-Default: ``null``
-
-
-.. _javascript-highlightMatch:
-
-highlightMatch
-______________________________________
-
-
-If true the portions of the label which match the current search term will be wrapped
-in a span with the class ``highlight``.
-
-Default: ``true``
-
-
-.. _javascript-formatLabel:
-
-formatLabel
-______________________________________
-
-
-``formatLabel`` is a function that is run prior to rendering the search results in
-the dropdown menu. It takes two arguments: the current item label and the item data
-dictionary. It should return the label which should be used. For more information
-on its usage see :ref:`Label Formats on the Client Side <advanced-label-formats>`.
-
-Default: ``null``
-
diff --git a/dep/django-selectable/docs/testing.rst b/dep/django-selectable/docs/testing.rst
deleted file mode 100644 (file)
index 234b4ac..0000000
+++ /dev/null
@@ -1,153 +0,0 @@
-Testing Forms and Lookups
-====================================
-
-django-selectable has its own test suite for testing the rendering, validation
-and server-side logic it provides. However, depending on the additional customizations
-you add to your forms and lookups you most likely will want to include tests of your
-own. This section contains some tips or techniques for testing your lookups.
-
-This guide assumes that you are reasonable familiar with the concepts of unit testing
-including Python's `unittest <http://docs.python.org/2/library/unittest.html>`_ module and
-Django's `testing guide <https://docs.djangoproject.com/en/stable/topics/testing/>`_.
-
-
-Testing Forms with django-selectable
---------------------------------------------------
-
-For the most part testing forms which use django-selectable's custom fields
-and widgets is the same as testing any Django form. One point that is slightly
-different is that the select and multi-select widgets are
-`MultiWidgets <https://docs.djangoproject.com/en/stable/ref/forms/widgets/#django.forms.MultiWidget>`_.
-The effect of this is that there are two names in the post rather than one. Take the below
-form for example.
-
-    .. code-block:: python
-
-        # models.py
-
-        from django.db import models
-
-        class Thing(models.Model):
-            name = models.CharField(max_length=100)
-            description = models.CharField(max_length=100)
-
-            def __unicode__(self):
-                return self.name
-
-    .. code-block:: python
-
-        # lookups.py
-
-        from selectable.base import ModelLookup
-        from selectable.registry import registry
-
-        from .models import Thing
-
-        class ThingLookup(ModelLookup):
-            model = Thing
-            search_fields = ('name__icontains', )
-
-        registry.register(ThingLookup)
-
-    .. code-block:: python
-
-        # forms.py
-
-        from django import forms
-
-        from selectable.forms import AutoCompleteSelectField
-
-        from .lookups import ThingLookup
-
-        class SimpleForm(forms.Form):
-            "Basic form for testing."
-            thing = AutoCompleteSelectField(lookup_class=ThingLookup)
-
-This form has a single field to select a ``Thing``. It does not allow
-new items. Let's write some simple tests for this form.
-
-    .. code-block:: python
-
-        # tests.py
-
-        from django.test import TestCase
-
-        from .forms import SimpleForm
-        from .models import Thing
-
-        class SimpleFormTestCase(TestCase):
-
-            def test_valid_form(self):
-                "Submit valid data."
-                thing = Thing.objects.create(name='Foo', description='Bar')
-                data = {
-                    'thing_0': thing.name,
-                    'thing_1': thing.pk,
-                }
-                form = SimpleForm(data=data)
-                self.assertTrue(form.is_valid())
-
-            def test_invalid_form(self):
-                "Thing is required but missing."
-                data = {
-                    'thing_0': 'Foo',
-                    'thing_1': '',
-                }
-                form = SimpleForm(data=data)
-                self.assertFalse(form.is_valid())
-
-Here you will note that while there is only one field ``thing`` it requires
-two items in the POST the first is for the text input and the second is for
-the hidden input. This is again due to the use of MultiWidget for the selection.
-
-There is compatibility code in the widgets to lookup the original name
-from the POST. This makes it easier to transition to the the selectable widgets without
-breaking existing tests.
-
-
-Testing Lookup Results
---------------------------------------------------
-
-Testing the lookups used by django-selectable is similar to testing your Django views.
-While it might be tempting to use the Django
-`test client <https://docs.djangoproject.com/en/stable/topics/testing/#module-django.test.client>`_,
-it is slightly easier to use the
-`request factory <https://docs.djangoproject.com/en/stable/topics/testing/#the-request-factory>`_.
-A simple example is given below.
-
-    .. code-block:: python
-
-        # tests.py
-
-        import json
-
-        from django.test import TestCase
-        from django.test.client import RequestFactory
-
-        from .lookups import ThingLookup
-        from .models import Thing
-
-        class ThingLookupTestCase(TestCase):
-
-            def setUp(self):
-                self.factory = RequestFactory()
-                self.lookup = ThingLookup()
-                self.test_thing = Thing.objects.create(name='Foo', description='Bar')
-
-            def test_results(self):
-                "Test full response."
-                request = self.factory.get("/", {'term': 'Fo'})
-                response = self.lookup.results(request)
-                data = json.loads(response.content)['data']
-                self.assertEqual(1, len(data))
-                self.assertEqual(self.test_thing.pk, data[1]['id'])
-
-            def test_label(self):
-                "Test item label."
-                label = self.lookup.get_item_label(self.test_thing)
-                self.assertEqual(self.test_thing.name, label)
-
-As shown in the ``test_label`` example it is not required to test the full
-request/response. You can test each of the methods in the lookup API individually.
-When testing your lookups you should focus on testing the portions which have been
-customized by your application.
\ No newline at end of file
diff --git a/dep/django-selectable/docs/widgets.rst b/dep/django-selectable/docs/widgets.rst
deleted file mode 100644 (file)
index 2235438..0000000
+++ /dev/null
@@ -1,116 +0,0 @@
-Widgets
-==========
-
-Below are the custom widgets defined by Django-Selectable. All widgets take the
-lookup class as the first required argument.
-
-These widgets all support a ``query_params`` keyword argument which is used to pass
-additional query parameters to the lookup search. See the section on
-:ref:`Adding Parameters on the Server Side <server-side-parameters>` for more
-information.
-
-You can configure the plugin options by passing the configuration dictionary in the ``data-selectable-options``
-attribute. The set of options availble include those define by the base
-`autocomplete plugin <http://api.jqueryui.com/1.9/autocomplete/>`_ as well as the
-:ref:`javascript-removeIcon`, :ref:`javascript-comboboxIcon`, and :ref:`javascript-highlightMatch` options
-which are unique to django-selectable.
-
-    .. code-block:: python
-
-        attrs = {'data-selectable-options': {'highlightMatch': True, 'minLength': 5}}
-        selectable.AutoCompleteSelectWidget(lookup_class=FruitLookup, attrs=attrs)
-
-
-.. _AutoCompleteWidget:
-
-AutoCompleteWidget
---------------------------------------
-
-Basic widget for auto-completing text. The widget returns the item value as defined
-by the lookup ``get_item_value``. If the ``allow_new`` keyword argument is passed as
-true it will allow the user to type any text they wish.
-
-.. _AutoComboboxWidget:
-
-AutoComboboxWidget
---------------------------------------
-
-Similar to :ref:`AutoCompleteWidget` but has a button to reveal all options.
-
-
-.. _AutoCompleteSelectWidget:
-
-AutoCompleteSelectWidget
---------------------------------------
-
-Widget for selecting a value/id based on input text. Optionally allows selecting new items to be created.
-This widget should be used in conjunction with the :ref:`AutoCompleteSelectField` as it will
-return both the text entered by the user and the id (if an item was selected/matched).
-
-:ref:`AutoCompleteSelectWidget` works directly with Django's
-`ModelChoiceField <https://docs.djangoproject.com/en/stable/ref/forms/fields/#modelchoicefield>`_.
-You can simply replace the widget without replacing the entire field.
-
-    .. code-block:: python
-
-        class FarmAdminForm(forms.ModelForm):
-
-            class Meta(object):
-                model = Farm
-                widgets = {
-                    'owner': selectable.AutoCompleteSelectWidget(lookup_class=FruitLookup),
-                }
-
-The one catch is that you must use ``allow_new=False`` which is the default.
-
-``lookup_class`` may also be a dotted path.
-
-    .. code-block:: python
-
-         widget = selectable.AutoCompleteWidget(lookup_class='core.lookups.FruitLookup')
-
-
-.. _AutoComboboxSelectWidget:
-
-AutoComboboxSelectWidget
---------------------------------------
-
-Similar to :ref:`AutoCompleteSelectWidget` but has a button to reveal all options.
-
-:ref:`AutoComboboxSelectWidget` works directly with Django's
-`ModelChoiceField <https://docs.djangoproject.com/en/stable/ref/forms/fields/#modelchoicefield>`_.
-You can simply replace the widget without replacing the entire field.
-
-    .. code-block:: python
-
-        class FarmAdminForm(forms.ModelForm):
-
-            class Meta(object):
-                model = Farm
-                widgets = {
-                    'owner': selectable.AutoComboboxSelectWidget(lookup_class=FruitLookup),
-                }
-
-The one catch is that you must use ``allow_new=False`` which is the default.
-
-
-.. _AutoCompleteSelectMultipleWidget:
-
-AutoCompleteSelectMultipleWidget
---------------------------------------
-
-Builds a list of selected items from auto-completion. This widget will return a list
-of item ids as defined by the lookup ``get_item_id``. Using this widget with the
-:ref:`AutoCompleteSelectMultipleField` will clean the items to the item objects. This does
-not allow for creating new items. There is another optional keyword argument ``postion``
-which can take four possible values: `bottom`, `bottom-inline`, `top` or `top-inline`.
-This determine the position of the deck list of currently selected items as well as
-whether this list is stacked or inline. The default is `bottom`.
-
-
-.. _AutoComboboxSelectMultipleWidget:
-
-AutoComboboxSelectMultipleWidget
---------------------------------------
-
-Same as :ref:`AutoCompleteSelectMultipleWidget` but with a combobox.
diff --git a/dep/django-selectable/runtests.py b/dep/django-selectable/runtests.py
deleted file mode 100755 (executable)
index a06c445..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-#!/usr/bin/env python
-import os
-import sys
-
-from django.conf import settings
-
-
-if not settings.configured:
-    settings.configure(
-        DATABASES={
-            'default': {
-                'ENGINE': 'django.db.backends.sqlite3',
-                'NAME': ':memory:',
-            }
-        },
-        MIDDLEWARE=(),
-        INSTALLED_APPS=(
-            'selectable',
-        ),
-        SECRET_KEY='super-secret',
-        ROOT_URLCONF='selectable.tests.urls',
-        TEMPLATES=[{
-            'BACKEND': 'django.template.backends.django.DjangoTemplates',
-            'DIRS': [os.path.join(os.path.normpath(os.path.join(
-                os.path.dirname(__file__), 'selectable')), 'templates')]}])
-
-
-from django import setup
-from django.test.utils import get_runner
-
-
-def runtests():
-    setup()
-    TestRunner = get_runner(settings)
-    test_runner = TestRunner(verbosity=1, interactive=True, failfast=False)
-    args = sys.argv[1:] or ['selectable', ]
-    failures = test_runner.run_tests(args)
-    sys.exit(failures)
-
-
-if __name__ == '__main__':
-    runtests()
diff --git a/dep/django-selectable/selectable/__init__.py b/dep/django-selectable/selectable/__init__.py
deleted file mode 100644 (file)
index a93d9cc..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-"Auto-complete selection widgets using Django and jQuery UI."
-
-
-__version__ = '1.2.1'
-
-default_app_config = 'selectable.apps.SelectableConfig'
diff --git a/dep/django-selectable/selectable/apps.py b/dep/django-selectable/selectable/apps.py
deleted file mode 100644 (file)
index 579bf78..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-from django.apps import AppConfig
-
-
-class SelectableConfig(AppConfig):
-    """App configuration for django-selectable."""
-
-    name = 'selectable'
-
-    def ready(self):
-        from . import registry
-        registry.autodiscover()
diff --git a/dep/django-selectable/selectable/base.py b/dep/django-selectable/selectable/base.py
deleted file mode 100644 (file)
index 9570a19..0000000
+++ /dev/null
@@ -1,165 +0,0 @@
-"Base classes for lookup creation."
-from __future__ import unicode_literals
-
-import operator
-import re
-from functools import reduce
-
-from django.conf import settings
-from django.core.paginator import Paginator, InvalidPage, EmptyPage
-from django.http import JsonResponse
-from django.db.models import Q, Model
-from django.urls import reverse
-from django.utils.encoding import smart_text
-from django.utils.html import conditional_escape
-from django.utils.translation import ugettext as _
-
-from .forms import BaseLookupForm
-
-__all__ = (
-    'LookupBase',
-    'ModelLookup',
-)
-
-
-class LookupBase(object):
-    "Base class for all django-selectable lookups."
-
-    form = BaseLookupForm
-    response = JsonResponse
-
-    def _name(cls):
-        app_name = cls.__module__.split('.')[-2].lower()
-        class_name = cls.__name__.lower()
-        name = '%s-%s' % (app_name, class_name)
-        return name
-    name = classmethod(_name)
-
-    def split_term(self, term):
-        """
-        Split searching term into array of subterms
-        that will be searched separately.
-        """
-        return term.split()
-
-    def _url(cls):
-        return reverse('selectable-lookup', args=[cls.name()])
-    url = classmethod(_url)
-
-    def get_query(self, request, term):
-        return []
-
-    def get_item_label(self, item):
-        return smart_text(item)
-
-    def get_item_id(self, item):
-        return smart_text(item)
-
-    def get_item_value(self, item):
-        return smart_text(item)
-
-    def get_item(self, value):
-        return value
-
-    def create_item(self, value):
-        raise NotImplemented()
-
-    def format_item(self, item):
-        "Construct result dictionary for the match item."
-        result = {
-            'id': self.get_item_id(item),
-            'value': self.get_item_value(item),
-            'label': self.get_item_label(item),
-        }
-        for key in settings.SELECTABLE_ESCAPED_KEYS:
-            if key in result:
-                result[key] = conditional_escape(result[key])
-        return result
-
-    def paginate_results(self, results, options):
-        "Return a django.core.paginator.Page of results."
-        limit = options.get('limit', settings.SELECTABLE_MAX_LIMIT)
-        paginator = Paginator(results, limit)
-        page = options.get('page', 1)
-        try:
-            results = paginator.page(page)
-        except (EmptyPage, InvalidPage):
-            results = paginator.page(paginator.num_pages)
-        return results
-
-    def results(self, request):
-        "Match results to given term and return the serialized HttpResponse."
-        results = {}
-        form = self.form(request.GET)
-        if form.is_valid():
-            options = form.cleaned_data
-            term = options.get('term', '')
-            raw_data = self.get_query(request, term)
-            results = self.format_results(raw_data, options)
-        return self.response(results)
-
-    def format_results(self, raw_data, options):
-        '''
-        Returns a python structure that later gets serialized.
-        raw_data
-            full list of objects matching the search term
-        options
-            a dictionary of the given options
-        '''
-        page_data = self.paginate_results(raw_data, options)
-        results = {}
-        meta = options.copy()
-        meta['more'] = _('Show more results')
-        if page_data and page_data.has_next():
-            meta['next_page'] = page_data.next_page_number()
-        if page_data and page_data.has_previous():
-            meta['prev_page'] = page_data.previous_page_number()
-        results['data'] = [self.format_item(item) for item in page_data.object_list]
-        results['meta'] = meta
-        return results
-
-
-class ModelLookup(LookupBase):
-    "Lookup class for easily defining lookups based on Django models."
-
-    model = None
-    filters = {}
-    search_fields = ()
-
-    def get_query(self, request, term):
-        qs = self.get_queryset()
-        if term:
-            if self.search_fields:
-                for t in self.split_term(term):
-                    search_filters = []
-                    for field in self.search_fields:
-                        search_filters.append(Q(**{field: t}))
-                    qs = qs.filter(reduce(operator.or_, search_filters))
-        return qs
-
-    def get_queryset(self):
-        qs = self.model._default_manager.get_queryset()
-        if self.filters:
-            qs = qs.filter(**self.filters)
-        return qs
-
-    def get_item_id(self, item):
-        return item.pk
-
-    def get_item(self, value):
-        item = None
-        if value:
-            value = value.pk if isinstance(value, Model) else value
-            try:
-                item = self.get_queryset().get(pk=value)
-            except (ValueError, self.model.DoesNotExist):
-                item = None
-        return item
-
-    def create_item(self, value):
-        data = {}
-        if self.search_fields:
-            field_name = re.sub(r'__\w+$', '',  self.search_fields[0])
-            if field_name:
-                data = {field_name: value}
-        return self.model(**data)
diff --git a/dep/django-selectable/selectable/compat.py b/dep/django-selectable/selectable/compat.py
deleted file mode 100644 (file)
index c438c85..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-"Compatibility utilites for Python versions."
-
-try:
-    from urllib.parse import urlparse
-except ImportError:
-    # This can be removed when Python 2.7 support is dropped
-    from urlparse import urlparse
diff --git a/dep/django-selectable/selectable/decorators.py b/dep/django-selectable/selectable/decorators.py
deleted file mode 100644 (file)
index 3245383..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-"Decorators for additional lookup functionality."
-from __future__ import unicode_literals
-
-from functools import wraps
-
-from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseForbidden
-
-
-__all__ = (
-    'ajax_required',
-    'login_required',
-    'staff_member_required',
-)
-
-
-def results_decorator(func):
-    """
-    Helper for constructing simple decorators around Lookup.results.
-
-    func is a function which takes a request as the first parameter. If func
-    returns an HttpReponse it is returned otherwise the original Lookup.results
-    is returned.
-    """
-    # Wrap function to maintian the original doc string, etc
-    @wraps(func)
-    def decorator(lookup_cls):
-        # Construct a class decorator from the original function
-        original = lookup_cls.results
-        def inner(self, request):
-            # Wrap lookup_cls.results by first calling func and checking the result
-            result = func(request)
-            if isinstance(result, HttpResponse):
-                return result
-            return original(self, request)
-        # Replace original lookup_cls.results with wrapped version
-        lookup_cls.results = inner
-        return lookup_cls
-    # Return the constructed decorator
-    return decorator
-
-
-@results_decorator
-def ajax_required(request):
-    "Lookup decorator to require AJAX calls to the lookup view."
-    if not request.is_ajax():
-        return HttpResponseBadRequest()
-
-
-@results_decorator
-def login_required(request):
-    "Lookup decorator to require the user to be authenticated."
-    user = getattr(request, 'user', None)
-    if user is None or not user.is_authenticated:
-        return HttpResponse(status=401) # Unauthorized
-
-
-@results_decorator
-def staff_member_required(request):
-    "Lookup decorator to require the user is a staff member."
-    user = getattr(request, 'user', None)
-    if user is None or not user.is_authenticated:
-        return HttpResponse(status=401) # Unauthorized
-    elif not user.is_staff:
-        return HttpResponseForbidden()
diff --git a/dep/django-selectable/selectable/exceptions.py b/dep/django-selectable/selectable/exceptions.py
deleted file mode 100644 (file)
index 71b39d9..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-class LookupAlreadyRegistered(Exception):
-    "Exception when trying to register a lookup which is already registered."
-
-
-class LookupNotRegistered(Exception):
-    "Exception when trying use a lookup which is not registered."
-
-
-class LookupInvalid(Exception):
-    "Exception when register an invalid lookup class."
diff --git a/dep/django-selectable/selectable/forms/__init__.py b/dep/django-selectable/selectable/forms/__init__.py
deleted file mode 100644 (file)
index cc950ad..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-from selectable.forms.base import *
-from selectable.forms.fields import *
-from selectable.forms.widgets import *
diff --git a/dep/django-selectable/selectable/forms/base.py b/dep/django-selectable/selectable/forms/base.py
deleted file mode 100644 (file)
index b2abb0d..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-from __future__ import unicode_literals
-
-from importlib import import_module
-
-from django import forms
-from django.conf import settings
-from django.utils.six import string_types
-
-
-__all__ = (
-    'BaseLookupForm',
-    'import_lookup_class',
-)
-
-
-class BaseLookupForm(forms.Form):
-    term = forms.CharField(required=False)
-    limit = forms.IntegerField(required=False, min_value=1)
-    page = forms.IntegerField(required=False, min_value=1)
-
-    def clean_limit(self):
-        "Ensure given limit is less than default if defined"
-        limit = self.cleaned_data.get('limit', None)
-        if (settings.SELECTABLE_MAX_LIMIT is not None and
-            (not limit or limit > settings.SELECTABLE_MAX_LIMIT)):
-            limit = settings.SELECTABLE_MAX_LIMIT
-        return limit
-
-    def clean_page(self):
-        "Return the first page if no page or invalid number is given."
-        return self.cleaned_data.get('page', 1) or 1
-
-
-def import_lookup_class(lookup_class):
-    """
-    Import lookup_class as a dotted base and ensure it extends LookupBase
-    """
-    from selectable.base import LookupBase
-    if isinstance(lookup_class, string_types):
-        mod_str, cls_str = lookup_class.rsplit('.', 1)
-        mod = import_module(mod_str)
-        lookup_class = getattr(mod, cls_str)
-    if not issubclass(lookup_class, LookupBase):
-        raise TypeError('lookup_class must extend from selectable.base.LookupBase')
-    return lookup_class
diff --git a/dep/django-selectable/selectable/forms/fields.py b/dep/django-selectable/selectable/forms/fields.py
deleted file mode 100644 (file)
index 45e7da1..0000000
+++ /dev/null
@@ -1,117 +0,0 @@
-from __future__ import unicode_literals
-
-from django import forms
-from django.core.exceptions import ValidationError
-from django.core.validators import EMPTY_VALUES
-from django.utils.translation import ugettext_lazy as _
-from django.db.models import Model
-
-from selectable.forms.base import import_lookup_class
-from selectable.forms.widgets import AutoCompleteSelectWidget
-from selectable.forms.widgets import AutoCompleteSelectMultipleWidget
-
-__all__ = (
-    'AutoCompleteSelectField',
-    'AutoCompleteSelectMultipleField',
-)
-
-
-def model_vars(obj):
-    fields = dict(
-        (field.name, getattr(obj, field.name))
-        for field in obj._meta.fields
-    )
-    return fields
-
-
-class BaseAutoCompleteField(forms.Field):
-
-    def has_changed(self, initial, data):
-        "Detects if the data was changed. This is added in 1.6."
-        if initial is None and data is None:
-            return False
-        if data and not hasattr(data, '__iter__'):
-            data = self.widget.decompress(data)
-        initial = self.to_python(initial)
-        data = self.to_python(data)
-        if hasattr(self, '_coerce'):
-            data = self._coerce(data)
-        if isinstance(data, Model) and isinstance(initial, Model):
-            return model_vars(data) != model_vars(initial)
-        else:
-            return data != initial
-
-
-class AutoCompleteSelectField(BaseAutoCompleteField):
-    widget = AutoCompleteSelectWidget
-
-    default_error_messages = {
-        'invalid_choice': _('Select a valid choice. That choice is not one of the available choices.'),
-    }
-
-    def __init__(self, lookup_class, *args, **kwargs):
-        self.lookup_class = import_lookup_class(lookup_class)
-        self.allow_new = kwargs.pop('allow_new', False)
-        self.limit = kwargs.pop('limit', None)
-        widget = kwargs.get('widget', self.widget) or self.widget
-        if isinstance(widget, type):
-            kwargs['widget'] = widget(lookup_class, allow_new=self.allow_new, limit=self.limit)
-        super(AutoCompleteSelectField, self).__init__(*args, **kwargs)
-
-    def to_python(self, value):
-        if value in EMPTY_VALUES:
-            return None
-        lookup = self.lookup_class()
-        if isinstance(value, list):
-            # Input comes from an AutoComplete widget. It's two
-            # components: text and id
-            if len(value) != 2:
-                raise ValidationError(self.error_messages['invalid_choice'])
-            label, pk = value
-            if pk in EMPTY_VALUES:
-                if not self.allow_new:
-                    if label:
-                        raise ValidationError(self.error_messages['invalid_choice'])
-                    else:
-                        return None
-                if label in EMPTY_VALUES:
-                    return None
-                value = lookup.create_item(label)
-            else:
-                value = lookup.get_item(pk)
-                if value is None:
-                    raise ValidationError(self.error_messages['invalid_choice'])
-        else:
-            value = lookup.get_item(value)
-            if value is None:
-                raise ValidationError(self.error_messages['invalid_choice'])
-        return value
-
-
-class AutoCompleteSelectMultipleField(BaseAutoCompleteField):
-    widget = AutoCompleteSelectMultipleWidget
-
-    default_error_messages = {
-        'invalid_choice': _('Select a valid choice. That choice is not one of the available choices.'),
-    }
-
-    def __init__(self, lookup_class, *args, **kwargs):
-        self.lookup_class = import_lookup_class(lookup_class)
-        self.limit = kwargs.pop('limit', None)
-        widget = kwargs.get('widget', self.widget) or self.widget
-        if isinstance(widget, type):
-            kwargs['widget'] = widget(lookup_class, limit=self.limit)
-        super(AutoCompleteSelectMultipleField, self).__init__(*args, **kwargs)
-
-    def to_python(self, value):
-        if value in EMPTY_VALUES:
-            return []
-        lookup = self.lookup_class()
-        items = []
-        for v in value:
-            if v not in EMPTY_VALUES:
-                item = lookup.get_item(v)
-                if item is None:
-                    raise ValidationError(self.error_messages['invalid_choice'])
-                items.append(item)
-        return items
diff --git a/dep/django-selectable/selectable/forms/widgets.py b/dep/django-selectable/selectable/forms/widgets.py
deleted file mode 100644 (file)
index bdc8b01..0000000
+++ /dev/null
@@ -1,248 +0,0 @@
-from __future__ import unicode_literals
-
-import json
-
-from django import forms
-from django.conf import settings
-from django.utils.encoding import force_text
-from django.utils.http import urlencode
-
-from selectable import __version__
-from selectable.forms.base import import_lookup_class
-
-__all__ = (
-    'AutoCompleteWidget',
-    'AutoCompleteSelectWidget',
-    'AutoComboboxWidget',
-    'AutoComboboxSelectWidget',
-    'AutoCompleteSelectMultipleWidget',
-    'AutoComboboxSelectMultipleWidget',
-)
-
-
-STATIC_PREFIX = '%sselectable/' % settings.STATIC_URL
-
-
-class SelectableMediaMixin(object):
-
-    class Media(object):
-        css = {
-            'all': ('%scss/dj.selectable.css?v=%s' % (STATIC_PREFIX, __version__),)
-        }
-        js = ('%sjs/jquery.dj.selectable.js?v=%s' % (STATIC_PREFIX, __version__),)
-
-
-class AutoCompleteWidget(forms.TextInput, SelectableMediaMixin):
-
-    def __init__(self, lookup_class, *args, **kwargs):
-        self.lookup_class = import_lookup_class(lookup_class)
-        self.allow_new = kwargs.pop('allow_new', False)
-        self.qs = kwargs.pop('query_params', {})
-        self.limit = kwargs.pop('limit', None)
-        super(AutoCompleteWidget, self).__init__(*args, **kwargs)
-
-    def update_query_parameters(self, qs_dict):
-        self.qs.update(qs_dict)
-
-    def build_attrs(self, base_attrs, extra_attrs=None):
-        attrs = super(AutoCompleteWidget, self).build_attrs(base_attrs, extra_attrs)
-        url = self.lookup_class.url()
-        if self.limit and 'limit' not in self.qs:
-            self.qs['limit'] = self.limit
-        if self.qs:
-            url = '%s?%s' % (url, urlencode(self.qs))
-        if 'data-selectable-options' in attrs:
-            attrs['data-selectable-options'] = json.dumps(attrs['data-selectable-options'])
-        attrs['data-selectable-url'] = url
-        attrs['data-selectable-type'] = 'text'
-        attrs['data-selectable-allow-new'] = str(self.allow_new).lower()
-        return attrs
-
-
-class SelectableMultiWidget(forms.MultiWidget):
-
-    def update_query_parameters(self, qs_dict):
-        self.widgets[0].update_query_parameters(qs_dict)
-
-    def decompress(self, value):
-        if value:
-            lookup = self.lookup_class()
-            model = getattr(self.lookup_class, 'model', None)
-            if model and isinstance(value, model):
-                item = value
-                value = lookup.get_item_id(item)
-            else:
-                item = lookup.get_item(value)
-            item_value = lookup.get_item_value(item)
-            return [item_value, value]
-        return [None, None]
-
-    def get_compatible_postdata(self, data, name):
-        """Get postdata built for a normal <select> element.
-
-        Django MultiWidgets create post variables like ``foo_0`` and ``foo_1``,
-        and this behavior is not cleanly overridable.  Non-multiwidgets, like
-        Select, get simple names like ``foo``. In order to keep this widget
-        compatible with requests designed for traditional select widgets,
-        search postdata for a name like ``foo`` and return that value.
-
-        This will return ``None`` if a ``<select>``-compatibile post variable
-        is not found.
-        """
-        return data.get(name, None)
-
-
-class _BaseSingleSelectWidget(SelectableMultiWidget, SelectableMediaMixin):
-    """
-    Common base class for AutoCompleteSelectWidget and AutoComboboxSelectWidget
-    each which use one widget (primary_widget) to select text and a single
-    hidden input to hold the selected id.
-    """
-
-    primary_widget = None
-
-    def __init__(self, lookup_class, *args, **kwargs):
-        self.lookup_class = import_lookup_class(lookup_class)
-        self.allow_new = kwargs.pop('allow_new', False)
-        self.limit = kwargs.pop('limit', None)
-        query_params = kwargs.pop('query_params', {})
-        widgets = [
-            self.primary_widget(
-                lookup_class, allow_new=self.allow_new,
-                limit=self.limit, query_params=query_params,
-                attrs=kwargs.get('attrs'),
-            ),
-            forms.HiddenInput(attrs={'data-selectable-type': 'hidden'})
-        ]
-        super(_BaseSingleSelectWidget, self).__init__(widgets, *args, **kwargs)
-
-    def value_from_datadict(self, data, files, name):
-        value = super(_BaseSingleSelectWidget, self).value_from_datadict(data, files, name)
-        if not value[1]:
-            compatible_postdata = self.get_compatible_postdata(data, name)
-            if compatible_postdata:
-                value[1] = compatible_postdata
-        if not self.allow_new:
-            return value[1]
-        return value
-
-
-class AutoCompleteSelectWidget(_BaseSingleSelectWidget):
-
-    primary_widget = AutoCompleteWidget
-
-
-class AutoComboboxWidget(AutoCompleteWidget, SelectableMediaMixin):
-
-    def build_attrs(self, base_attrs, extra_attrs=None):
-        attrs = super(AutoComboboxWidget, self).build_attrs(base_attrs, extra_attrs)
-        attrs['data-selectable-type'] = 'combobox'
-        return attrs
-
-
-class AutoComboboxSelectWidget(_BaseSingleSelectWidget):
-
-    primary_widget = AutoComboboxWidget
-
-
-class LookupMultipleHiddenInput(forms.MultipleHiddenInput):
-
-    def __init__(self, lookup_class, *args, **kwargs):
-        self.lookup_class = import_lookup_class(lookup_class)
-        super(LookupMultipleHiddenInput, self).__init__(*args, **kwargs)
-
-    def get_context(self, name, value, attrs):
-        lookup = self.lookup_class()
-        values = self._normalize_value(value)
-        values = list(values)  # force evaluation
-
-        context = super(LookupMultipleHiddenInput, self).get_context(name, values, attrs)
-
-        # Now override/add to what super() did:
-        subwidgets = context['widget']['subwidgets']
-        for widget_ctx, item in zip(subwidgets, values):
-            input_value, title = self._lookup_value_and_title(lookup, item)
-            widget_ctx['value'] = input_value  # override what super() did
-            if title:
-                widget_ctx['attrs']['title'] = title
-        return context
-
-    def build_attrs(self, base_attrs, extra_attrs=None):
-        attrs = super(LookupMultipleHiddenInput, self).build_attrs(base_attrs, extra_attrs)
-        attrs['data-selectable-type'] = 'hidden-multiple'
-        return attrs
-
-    def _normalize_value(self, value):
-        if value is None:
-            value = []
-        return value
-
-    def _lookup_value_and_title(self, lookup, v):
-        model = getattr(self.lookup_class, 'model', None)
-        item = None
-        if model and isinstance(v, model):
-            item = v
-            v = lookup.get_item_id(item)
-        title = None
-        if v:
-            item = item or lookup.get_item(v)
-            title = lookup.get_item_value(item)
-        return force_text(v), title
-
-
-class _BaseMultipleSelectWidget(SelectableMultiWidget, SelectableMediaMixin):
-    """
-    Common base class for AutoCompleteSelectMultipleWidget and AutoComboboxSelectMultipleWidget
-    each which use one widget (primary_widget) to select text and a multiple
-    hidden inputs to hold the selected ids.
-    """
-
-    primary_widget = None
-
-    def __init__(self, lookup_class, *args, **kwargs):
-        self.lookup_class = import_lookup_class(lookup_class)
-        self.limit = kwargs.pop('limit', None)
-        position = kwargs.pop('position', 'bottom')
-        attrs = {
-            'data-selectable-multiple': 'true',
-            'data-selectable-position': position
-        }
-        attrs.update(kwargs.get('attrs', {}))
-        query_params = kwargs.pop('query_params', {})
-        widgets = [
-            self.primary_widget(
-                lookup_class, allow_new=False,
-                limit=self.limit, query_params=query_params, attrs=attrs
-            ),
-            LookupMultipleHiddenInput(lookup_class)
-        ]
-        super(_BaseMultipleSelectWidget, self).__init__(widgets, *args, **kwargs)
-
-    def value_from_datadict(self, data, files, name):
-        value = self.widgets[1].value_from_datadict(data, files, name + '_1')
-        if not value:
-            # Fall back to the compatible POST name
-            value = self.get_compatible_postdata(data, name)
-        return value
-
-    def build_attrs(self, base_attrs, extra_attrs=None):
-        attrs = super(_BaseMultipleSelectWidget, self).build_attrs(base_attrs, extra_attrs)
-        if 'required' in attrs:
-            attrs.pop('required')
-        return attrs
-
-    def render(self, name, value, attrs=None, renderer=None):
-        if value and not hasattr(value, '__iter__'):
-            value = [value]
-        value = ['', value]
-        return super(_BaseMultipleSelectWidget, self).render(name, value, attrs, renderer)
-
-
-class AutoCompleteSelectMultipleWidget(_BaseMultipleSelectWidget):
-
-    primary_widget = AutoCompleteWidget
-
-
-class AutoComboboxSelectMultipleWidget(_BaseMultipleSelectWidget):
-
-    primary_widget = AutoComboboxWidget
diff --git a/dep/django-selectable/selectable/locale/en/LC_MESSAGES/django.mo b/dep/django-selectable/selectable/locale/en/LC_MESSAGES/django.mo
deleted file mode 100644 (file)
index 99d42ad..0000000
Binary files a/dep/django-selectable/selectable/locale/en/LC_MESSAGES/django.mo and /dev/null differ
diff --git a/dep/django-selectable/selectable/locale/en/LC_MESSAGES/django.po b/dep/django-selectable/selectable/locale/en/LC_MESSAGES/django.po
deleted file mode 100644 (file)
index 0f1f178..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
-#
-#, fuzzy
-msgid ""
-msgstr ""
-"Project-Id-Version: PACKAGE VERSION\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-10-21 20:14-0400\n"
-"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: LANGUAGE <[email protected]>\n"
-"Language: \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-
-#: base.py:117
-msgid "Show more results"
-msgstr ""
-
-#: forms/fields.py:48 forms/fields.py:96
-msgid "Select a valid choice. That choice is not one of the available choices."
-msgstr ""
diff --git a/dep/django-selectable/selectable/locale/es/LC_MESSAGES/django.mo b/dep/django-selectable/selectable/locale/es/LC_MESSAGES/django.mo
deleted file mode 100644 (file)
index 96ac6a1..0000000
Binary files a/dep/django-selectable/selectable/locale/es/LC_MESSAGES/django.mo and /dev/null differ
diff --git a/dep/django-selectable/selectable/locale/es/LC_MESSAGES/django.po b/dep/django-selectable/selectable/locale/es/LC_MESSAGES/django.po
deleted file mode 100644 (file)
index 6a6bc04..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-# 
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: django-selectable\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-10-06 15:02-0400\n"
-"PO-Revision-Date: 2013-11-20 10:18+0000\n"
-"Last-Translator: Manuel Alvarez <[email protected]>\n"
-"Language-Team: Spanish (http://www.transifex.com/projects/p/django-selectable/language/es/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: es\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#: base.py:115
-msgid "Show more results"
-msgstr "Mostrar más resultados"
-
-#: forms/fields.py:19 forms/fields.py:63
-msgid ""
-"Select a valid choice. That choice is not one of the available choices."
-msgstr "Seleccione una opción válida. La opción seleccionada no está disponible."
diff --git a/dep/django-selectable/selectable/locale/fr/LC_MESSAGES/django.mo b/dep/django-selectable/selectable/locale/fr/LC_MESSAGES/django.mo
deleted file mode 100644 (file)
index 651efbb..0000000
Binary files a/dep/django-selectable/selectable/locale/fr/LC_MESSAGES/django.mo and /dev/null differ
diff --git a/dep/django-selectable/selectable/locale/fr/LC_MESSAGES/django.po b/dep/django-selectable/selectable/locale/fr/LC_MESSAGES/django.po
deleted file mode 100644 (file)
index c1e265f..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-# 
-# Translators:
-# Mark Lavin <[email protected]>, 2014
-msgid ""
-msgstr ""
-"Project-Id-Version: django-selectable\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-10-06 15:02-0400\n"
-"PO-Revision-Date: 2014-01-21 01:00+0000\n"
-"Last-Translator: Mark Lavin <[email protected]>\n"
-"Language-Team: French (http://www.transifex.com/projects/p/django-selectable/language/fr/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: fr\n"
-"Plural-Forms: nplurals=2; plural=(n > 1);\n"
-
-#: base.py:115
-msgid "Show more results"
-msgstr "Afficher plus de résultats"
-
-#: forms/fields.py:19 forms/fields.py:63
-msgid ""
-"Select a valid choice. That choice is not one of the available choices."
-msgstr "Sélectionnez un choix valide. Ce choix ne fait pas partie de ceux disponibles."
diff --git a/dep/django-selectable/selectable/locale/pl/LC_MESSAGES/django.mo b/dep/django-selectable/selectable/locale/pl/LC_MESSAGES/django.mo
deleted file mode 100644 (file)
index 8e47890..0000000
Binary files a/dep/django-selectable/selectable/locale/pl/LC_MESSAGES/django.mo and /dev/null differ
diff --git a/dep/django-selectable/selectable/locale/pl/LC_MESSAGES/django.po b/dep/django-selectable/selectable/locale/pl/LC_MESSAGES/django.po
deleted file mode 100644 (file)
index 7ab61a4..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-# 
-# Translators:
-# slafs <[email protected]>, 2012
-msgid ""
-msgstr ""
-"Project-Id-Version: django-selectable\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-10-06 15:02-0400\n"
-"PO-Revision-Date: 2013-11-20 10:18+0000\n"
-"Last-Translator: slafs <[email protected]>\n"
-"Language-Team: Polish (http://www.transifex.com/projects/p/django-selectable/language/pl/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: pl\n"
-"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
-
-#: base.py:115
-msgid "Show more results"
-msgstr "Pokaż więcej wyników"
-
-#: forms/fields.py:19 forms/fields.py:63
-msgid ""
-"Select a valid choice. That choice is not one of the available choices."
-msgstr "Dokonaj poprawnego wyboru. Ten wybór nie jest jednym z dostępnych."
diff --git a/dep/django-selectable/selectable/locale/pt_BR/LC_MESSAGES/django.mo b/dep/django-selectable/selectable/locale/pt_BR/LC_MESSAGES/django.mo
deleted file mode 100644 (file)
index 6afef1b..0000000
Binary files a/dep/django-selectable/selectable/locale/pt_BR/LC_MESSAGES/django.mo and /dev/null differ
diff --git a/dep/django-selectable/selectable/locale/pt_BR/LC_MESSAGES/django.po b/dep/django-selectable/selectable/locale/pt_BR/LC_MESSAGES/django.po
deleted file mode 100644 (file)
index 3999877..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-# 
-# Translators:
-msgid ""
-msgstr ""
-"Project-Id-Version: django-selectable\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-10-06 15:02-0400\n"
-"PO-Revision-Date: 2013-11-20 10:18+0000\n"
-"Last-Translator: Mark Lavin <[email protected]>\n"
-"Language-Team: Portuguese (Brazil) (http://www.transifex.com/projects/p/django-selectable/language/pt_BR/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: pt_BR\n"
-"Plural-Forms: nplurals=2; plural=(n > 1);\n"
-
-#: base.py:115
-msgid "Show more results"
-msgstr "Mostrar mais resultados"
-
-#: forms/fields.py:19 forms/fields.py:63
-msgid ""
-"Select a valid choice. That choice is not one of the available choices."
-msgstr "Selecione uma escolha valida. Esta escolha não é uma das disponíveis."
diff --git a/dep/django-selectable/selectable/locale/zh_CN/LC_MESSAGES/django.mo b/dep/django-selectable/selectable/locale/zh_CN/LC_MESSAGES/django.mo
deleted file mode 100644 (file)
index 7bdf49c..0000000
Binary files a/dep/django-selectable/selectable/locale/zh_CN/LC_MESSAGES/django.mo and /dev/null differ
diff --git a/dep/django-selectable/selectable/locale/zh_CN/LC_MESSAGES/django.po b/dep/django-selectable/selectable/locale/zh_CN/LC_MESSAGES/django.po
deleted file mode 100644 (file)
index ae693db..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-# 
-# Translators:
-# mozillazg <[email protected]>, 2013
-msgid ""
-msgstr ""
-"Project-Id-Version: django-selectable\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-10-06 15:02-0400\n"
-"PO-Revision-Date: 2013-11-21 05:08+0000\n"
-"Last-Translator: mozillazg <[email protected]>\n"
-"Language-Team: Chinese (China) (http://www.transifex.com/projects/p/django-selectable/language/zh_CN/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Language: zh_CN\n"
-"Plural-Forms: nplurals=1; plural=0;\n"
-
-#: base.py:115
-msgid "Show more results"
-msgstr "显示更多结果"
-
-#: forms/fields.py:19 forms/fields.py:63
-msgid ""
-"Select a valid choice. That choice is not one of the available choices."
-msgstr "请选择一个有效的选项。当前选项无效。"
diff --git a/dep/django-selectable/selectable/models.py b/dep/django-selectable/selectable/models.py
deleted file mode 100644 (file)
index 8dee5f3..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-from __future__ import unicode_literals
-
-from django.conf import settings
-
-# Set default settings
-if not hasattr(settings, 'SELECTABLE_MAX_LIMIT'):
-    settings.SELECTABLE_MAX_LIMIT = 25
-
-if not hasattr(settings, 'SELECTABLE_ESCAPED_KEYS'):
-    settings.SELECTABLE_ESCAPED_KEYS = ('label', )
diff --git a/dep/django-selectable/selectable/registry.py b/dep/django-selectable/selectable/registry.py
deleted file mode 100644 (file)
index 5a475af..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-from __future__ import unicode_literals
-
-from django.utils.encoding import force_text
-from django.utils.module_loading import autodiscover_modules
-
-from selectable.base import LookupBase
-from selectable.exceptions import (LookupAlreadyRegistered, LookupNotRegistered,
-                                    LookupInvalid)
-
-
-class LookupRegistry(object):
-
-    def __init__(self):
-        self._registry = {}
-
-    def validate(self, lookup):
-        if not issubclass(lookup, LookupBase):
-            raise LookupInvalid('Registered lookups must inherit from the LookupBase class')
-
-    def register(self, lookup):
-        self.validate(lookup)
-        name = force_text(lookup.name())
-        if name in self._registry:
-            raise LookupAlreadyRegistered('The name %s is already registered' % name)
-        self._registry[name] = lookup
-
-    def unregister(self, lookup):
-        self.validate(lookup)
-        name = force_text(lookup.name())
-        if name not in self._registry:
-           raise LookupNotRegistered('The name %s is not registered' % name)
-        del self._registry[name]
-
-    def get(self, key):
-        return self._registry.get(key, None)
-
-
-registry = LookupRegistry()
-
-
-def autodiscover():
-    # Attempt to import the app's lookups module.
-    autodiscover_modules('lookups', register_to=registry)
diff --git a/dep/django-selectable/selectable/static/selectable/css/dj.selectable.css b/dep/django-selectable/selectable/static/selectable/css/dj.selectable.css
deleted file mode 100644 (file)
index 4501ba6..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * django-selectable UI widget CSS
- * Source: https://github.com/mlavin/django-selectable
- * Docs: http://django-selectable.readthedocs.org/
- *
- * Copyright 2010-2014, Mark Lavin
- * BSD License
- *
-*/
-ul.selectable-deck, ul.ui-autocomplete {
-    list-style: none outside none;
-}
-ul.selectable-deck li.selectable-deck-item,
-ul.ui-autocomplete li.ui-menu-item {
-    margin: 0;
-    list-style-type: none;
-}
-ul.selectable-deck li.selectable-deck-item .selectable-deck-remove {
-    float: right;
-}
-ul.selectable-deck-bottom-inline,
-ul.selectable-deck-top-inline {
-    padding: 0;
-}
-ul.selectable-deck-bottom-inline li.selectable-deck-item,
-ul.selectable-deck-top-inline li.selectable-deck-item {
-    display: inline;
-}
-ul.selectable-deck-bottom-inline li.selectable-deck-item .selectable-deck-remove,
-ul.selectable-deck-top-inline li.selectable-deck-item .selectable-deck-remove {
-    margin-left: 0.4em;
-    display: inline;
-    float: none;
-}
-ul.ui-autocomplete li.ui-menu-item span.highlight {
-    font-weight: bold;
-}
-input.ui-combo-input {
-    margin-right: 0;
-    line-height: 1.3;
-}
-a.ui-combo-button {
-    margin-left: -1px;
-}
-a.ui-combo-button .ui-button-text {
-    padding: 0;
-}
diff --git a/dep/django-selectable/selectable/static/selectable/js/jquery.dj.selectable.js b/dep/django-selectable/selectable/static/selectable/js/jquery.dj.selectable.js
deleted file mode 100644 (file)
index 7555a41..0000000
+++ /dev/null
@@ -1,393 +0,0 @@
-/*jshint trailing:true, indent:4*/
-/*
- * django-selectable UI widget
- * Source: https://github.com/mlavin/django-selectable
- * Docs: http://django-selectable.readthedocs.org/
- *
- * Depends:
- *   - jQuery 1.7+
- *   - jQuery UI 1.8 widget factory
- *
- * Copyright 2010-2014, Mark Lavin
- * BSD License
- *
-*/
-(function ($) {
-
-       $.widget("ui.djselectable", $.ui.autocomplete, {
-
-        options: {
-            removeIcon: "ui-icon-close",
-            comboboxIcon: "ui-icon-triangle-1-s",
-            defaultClasses: {
-                "text": "ui-widget ui-widget-content ui-corner-all",
-                "combobox": "ui-widget ui-widget-content ui-corner-left ui-combo-input"
-            },
-            prepareQuery: null,
-            highlightMatch: true,
-            formatLabel: null
-        },
-
-        _initDeck: function () {
-            /* Create list display for currently selected items for multi-select */
-            var self = this;
-            var data = $(this.element).data();
-            var style = data.selectablePosition || data['selectable-position'] || 'bottom';
-            this.deck = $('<ul>').addClass('ui-widget selectable-deck selectable-deck-' + style);
-            if (style === 'bottom' || style === 'bottom-inline') {
-                $(this.element).after(this.deck);
-            } else {
-                $(this.element).before(this.deck);
-            }
-            $(self.hiddenMultipleSelector).each(function (i, input) {
-                self._addDeckItem(input);
-            });
-        },
-
-        _addDeckItem: function (input) {
-            /* Add new deck list item from a given hidden input */
-            var self = this,
-                li = $('<li>').addClass('selectable-deck-item'),
-                item = {element: self.element, input: input, wrapper: li, deck: self.deck},
-                button;
-            li.html($(input).attr('title'));
-            if (self._trigger("add", null, item) === false) {
-                input.remove();
-            } else {
-                button = this._removeButtonTemplate(item);
-                button.click(function (e) {
-                    e.preventDefault();
-                    if (self._trigger("remove", e, item) !== false) {
-                        $(input).remove();
-                        li.remove();
-                    }
-                });
-                li.append(button).appendTo(this.deck);
-            }
-        },
-
-        _removeButtonTemplate: function (item) {
-            var options = {
-                    icons: {
-                        primary: this.options.removeIcon
-                    },
-                    text: false,
-                    disabled: this.disabled
-                },
-                button = $('<a>')
-                .attr('href', '#')
-                .addClass('selectable-deck-remove')
-                .button(options);
-            return button;
-        },
-
-        select: function (item, event) {
-            /* Trigger selection of a given item.
-            Item should contain two properties: id and value
-            Event is the original select event if there is one.
-            Event should not be passed if triggered manually.
-            */
-            var $input = $(this.element);
-            $input.removeClass('ui-state-error');
-            this._setHidden(item);
-            if (item) {
-                if (this.allowMultiple) {
-                    $input.val("");
-                    this.term = "";
-                    if ($(this.hiddenMultipleSelector + '[value="' + item.id + '"]').length === 0) {
-                        var newInput = $('<input />', {
-                            'type': 'hidden',
-                            'name': this.hiddenName,
-                            'value': item.id,
-                            'title': item.value,
-                            'data-selectable-type': 'hidden-multiple'
-                        });
-                        $input.after(newInput);
-                        this._addDeckItem(newInput);
-                    }
-                    return false;
-                } else {
-                    $input.val(item.value);
-                    var ui = {item: item};
-                    if (typeof(event) === 'undefined' || event.type !== "djselectableselect") {
-                        this.element.trigger("djselectableselect", [ui ]);
-                    }
-                }
-            }
-        },
-
-        _setHidden: function (item) {
-            /* Set or clear single hidden input */
-            var $elem = $(this.hiddenSelector);
-            if (item && item.id) {
-                $elem.val(item.id);
-            } else {
-                $elem.val("");
-            }
-        },
-
-        _comboButtonTemplate: function (input) {
-            // Add show all items button
-            var options = {
-                    icons: {
-                        primary: this.options.comboboxIcon
-                    },
-                    text: false,
-                    disabled: this.disabled
-                },
-                button = $("<a>")
-                    .html("&nbsp;")
-                    .attr("tabIndex", -1)
-                    .attr("title", "Show All Items")
-                    .button(options)
-                    .removeClass("ui-corner-all")
-                    .addClass("ui-corner-right ui-button-icon ui-combo-button");
-            return button;
-        },
-
-        _create: function () {
-            /* Initialize a new selectable widget */
-            var self = this,
-            $input = $(this.element),
-            data = $input.data(),
-            options, button;
-            this.url = data.selectableUrl || data['selectable-url'];
-            this.allowNew = data.selectableAllowNew || data['selectable-allow-new'];
-            this.allowMultiple = data.selectableMultiple || data['selectable-multiple'];
-            this.textName = $input.attr('name');
-            this.hiddenName = this.textName.replace(new RegExp('_0$'), '_1');
-            this.hiddenSelector = ':input[data-selectable-type=hidden][name=' + this.hiddenName + ']';
-            this.hiddenMultipleSelector = ':input[data-selectable-type=hidden-multiple][name=' + this.hiddenName + ']';
-            this.selectableType = data.selectableType || data['selectable-type'];
-            this.disabled = $input.prop('disabled');
-            if (this.allowMultiple) {
-                this.allowNew = false;
-                $input.val("");
-                this._initDeck();
-            }
-            options = data.selectableOptions || data['selectable-options'];
-            if (options) {
-                this._setOptions(options);
-            }
-            // Call super-create
-            // This could be replaced by this._super() with jQuery UI 1.9
-            $.ui.autocomplete.prototype._create.call(this);
-            $input.addClass(this.options.defaultClasses[this.selectableType]);
-            // Additional work for combobox widgets
-            if (this.selectableType === 'combobox') {
-                // Add show all items button
-                button = this._comboButtonTemplate($input);
-                button.insertAfter($input).click(function (e) {
-                    e.preventDefault();
-                    // close if already visible
-                    if (self.widget().is(":visible")) {
-                        self.close();
-                    }
-                    // pass empty string as value to search for, displaying all results
-                    self._search("");
-                    $input.focus();
-                });
-            }
-        },
-
-        // Override the default source creation
-        _initSource: function () {
-            var self = this,
-                $input = $(this.element);
-            this.source = function dataSource(request, response) {
-                /* Custom data source to uses the lookup url with pagination
-                Adds hook for adjusting query parameters.
-                Includes timestamp to prevent browser caching the lookup. */
-                var now = new Date().getTime(),
-                    query = {term: request.term, timestamp: now},
-                    page = $input.data("page");
-                if (self.options.prepareQuery) {
-                    self.options.prepareQuery.apply(self, [query]);
-                }
-                if (page) {
-                    query.page = page;
-                }
-                function unwrapResponse(data) {
-                    var results = data.data,
-                        meta = data.meta;
-                    if (meta.next_page && meta.more) {
-                        results.push({
-                            id: '',
-                            value: '',
-                            label: meta.more,
-                            page: meta.next_page,
-                            term: request.term
-                        });
-                    }
-                    return response(results);
-                }
-                               $.getJSON(self.url, query, unwrapResponse);
-            };
-        },
-        // Override the default auto-complete render.
-        _renderItem: function (ul, item) {
-            /* Adds hook for additional formatting, allows HTML in the label,
-            highlights term matches and handles pagination. */
-            var label = item.label,
-                self = this,
-                $input = $(this.element),
-                re, html, li;
-            if (this.options.formatLabel && !item.page) {
-                label = this.options.formatLabel.apply(this, [label, item]);
-            }
-            if (this.options.highlightMatch && this.term && !item.page) {
-                re = new RegExp("(?![^&;]+;)(?!<[^<>]*)(" +
-                    $.ui.autocomplete.escapeRegex(this.term) +
-                    ")(?![^<>]*>)(?![^&;]+;)", "gi");
-                if (label.html) {
-                    html = label.html();
-                    html = html.replace(re, "<span class='highlight'>$1</span>");
-                    label.html(html);
-                } else {
-                    label = label.replace(re, "<span class='highlight'>$1</span>");
-                }
-            }
-            li = $("<li></li>")
-                .data("item.autocomplete", item)
-                .append($("<a></a>").append(label))
-                .appendTo(ul);
-            if (item.page) {
-                li.addClass('selectable-paginator');
-            }
-            return li;
-        },
-        // Override the default auto-complete suggest.
-        _suggest: function (items) {
-            /* Needed for handling pagination links */
-            var $input = $(this.element),
-                page = $input.data('page'),
-                ul = this.menu.element;
-            if (page) {
-                $('.selectable-paginator', ul).remove();
-            } else {
-                ul.empty();
-            }
-            $input.data('page', null);
-            ul.css("zIndex", $input.css("zIndex") + 1);
-            this._renderMenu(ul, items);
-            // jQuery UI menu does not define deactivate
-            if (this.menu.deactivate) {
-                this.menu.deactivate();
-            }
-            this.menu.refresh();
-            // size and position menu
-            ul.show();
-            this._resizeMenu();
-            ul.position($.extend({of: this.element}, this.options.position));
-            if (this.options.autoFocus) {
-                this.menu.next(new $.Event("mouseover"));
-            } else if (page) {
-                $input.focus();
-            }
-        },
-        // Override default trigger for additional change/select logic
-        _trigger: function (type, event, data) {
-            var $input = $(this.element),
-                self = this;
-            if (type === "select") {
-                $input.removeClass('ui-state-error');
-                if (data.item.page) {
-                    $input.data("page", data.item.page);
-                    this._search(data.item.term);
-                    return false;
-                }
-                return this.select(data.item, event);
-            } else if (type === "change") {
-                $input.removeClass('ui-state-error');
-                this._setHidden(data.item);
-                if ($input.val() && !data.item) {
-                    if (!this.allowNew) {
-                        $input.addClass('ui-state-error');
-                    }
-                }
-                if (this.allowMultiple && !$input.hasClass('ui-state-error')) {
-                    $input.val("");
-                    this.term = "";
-                }
-            }
-            // Call super trigger
-            // This could be replaced by this._super() with jQuery UI 1.9
-            return $.ui.autocomplete.prototype._trigger.apply(this, arguments);
-        },
-        close: function (event) {
-            var page = $(this.element).data('page');
-            if (page !== null) {
-                return;
-            }
-            // Call super trigger
-            // This could be replaced by this._super() with jQuery UI 1.9
-            return $.ui.autocomplete.prototype.close.apply(this, arguments);
-        }
-       });
-
-    window.bindSelectables = function (context) {
-        /* Bind all selectable widgets in a given context.
-        Automatically called on document.ready.
-        Additional calls can be made for dynamically added widgets.
-        */
-        $(":input[data-selectable-type=text]", context).djselectable();
-        $(":input[data-selectable-type=combobox]", context).djselectable();
-    };
-
-    function djangoAdminPatches() {
-        /* Listen for new rows being added to the dynamic inlines.
-        Requires Django 1.5+ */
-        $('body').on('click', '.add-row', function (e) {
-            var wrapper = $(this).parents('.inline-related'),
-                newRow = $('.form-row:not(.empty-form)', wrapper).last();
-            window.bindSelectables(newRow);
-        });
-
-        /* Monkey-patch Django's dismissAddAnotherPopup(), if defined */
-        if (typeof(dismissAddAnotherPopup) !== "undefined" &&
-            typeof(windowname_to_id) !== "undefined" &&
-            typeof(html_unescape) !== "undefined") {
-            var django_dismissAddAnotherPopup = dismissAddAnotherPopup;
-            dismissAddAnotherPopup = function (win, newId, newRepr) {
-                /* See if the popup came from a selectable field.
-                   If not, pass control to Django's code.
-                   If so, handle it. */
-                var fieldName = windowname_to_id(win.name); /* e.g. "id_fieldname" */
-                var field = $('#' + fieldName);
-                var multiField = $('#' + fieldName + '_0');
-                /* Check for bound selectable */
-                var singleWidget = field.data('djselectable');
-                var multiWidget = multiField.data('djselectable');
-                if (singleWidget || multiWidget) {
-                    // newId and newRepr are expected to have previously been escaped by
-                    // django.utils.html.escape.
-                    var item =  {
-                        id: html_unescape(newId),
-                        value: html_unescape(newRepr)
-                    };
-                    if (singleWidget) {
-                        field.djselectable('select', item);
-                    }
-                    if (multiWidget) {
-                        multiField.djselectable('select', item);
-                    }
-                    win.close();
-                } else {
-                    /* Not ours, pass on to original function. */
-                    return django_dismissAddAnotherPopup(win, newId, newRepr);
-                }
-            };
-        }
-    }
-
-    $(document).ready(function () {
-        // Patch the django admin JS
-        if (typeof(djselectableAdminPatch) === "undefined" || djselectableAdminPatch) {
-            djangoAdminPatches();
-        }
-        // Bind existing widgets on document ready
-        if (typeof(djselectableAutoLoad) === "undefined" || djselectableAutoLoad) {
-            window.bindSelectables('body');
-        }
-    });
-})(jQuery || grp.jQuery);
diff --git a/dep/django-selectable/selectable/templates/selectable/jquery-css.html b/dep/django-selectable/selectable/templates/selectable/jquery-css.html
deleted file mode 100644 (file)
index 76aa0c5..0000000
+++ /dev/null
@@ -1 +0,0 @@
-<link rel="stylesheet" href="//ajax.googleapis.com/ajax/libs/jqueryui/{{ version }}/themes/{{ theme }}/jquery-ui.css" type="text/css">
diff --git a/dep/django-selectable/selectable/templates/selectable/jquery-js.html b/dep/django-selectable/selectable/templates/selectable/jquery-js.html
deleted file mode 100644 (file)
index 1724365..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-{% if version %}<script src="//ajax.googleapis.com/ajax/libs/jquery/{{ version }}/jquery.min.js"></script>{% endif %}
-{% if ui %}<script src="//ajax.googleapis.com/ajax/libs/jqueryui/{{ ui }}/jquery-ui.js"></script>{% endif %}
diff --git a/dep/django-selectable/selectable/templatetags/__init__.py b/dep/django-selectable/selectable/templatetags/__init__.py
deleted file mode 100755 (executable)
index e69de29..0000000
diff --git a/dep/django-selectable/selectable/templatetags/selectable_tags.py b/dep/django-selectable/selectable/templatetags/selectable_tags.py
deleted file mode 100755 (executable)
index c380e53..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-from __future__ import unicode_literals
-
-from django import template
-
-register = template.Library()
-
-
[email protected]_tag('selectable/jquery-js.html')
-def include_jquery_libs(version='1.12.4', ui='1.11.4'):
-    return {'version': version, 'ui': ui}
-
-
[email protected]_tag('selectable/jquery-css.html')
-def include_ui_theme(theme='smoothness', version='1.11.4'):
-    return {'theme': theme, 'version': version}
diff --git a/dep/django-selectable/selectable/tests/__init__.py b/dep/django-selectable/selectable/tests/__init__.py
deleted file mode 100644 (file)
index b529c3c..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-from django.db import models
-from django.utils.encoding import python_2_unicode_compatible
-
-from ..base import ModelLookup
-from ..registry import registry
-
-
-@python_2_unicode_compatible
-class Thing(models.Model):
-    name = models.CharField(max_length=100)
-    description = models.CharField(max_length=100)
-
-    def __str__(self):
-        return self.name
-
-    class Meta:
-        ordering = ['id']
-
-
-@python_2_unicode_compatible
-class OtherThing(models.Model):
-    name = models.CharField(max_length=100)
-    thing = models.ForeignKey(Thing, on_delete=models.CASCADE)
-
-    def __str__(self):
-        return self.name
-
-
-@python_2_unicode_compatible
-class ManyThing(models.Model):
-    name = models.CharField(max_length=100)
-    things = models.ManyToManyField(Thing)
-
-    def __str__(self):
-        return self.name
-
-
-class ThingLookup(ModelLookup):
-    model = Thing
-    search_fields = ('name__icontains', )
-
-
-registry.register(ThingLookup)
diff --git a/dep/django-selectable/selectable/tests/base.py b/dep/django-selectable/selectable/tests/base.py
deleted file mode 100644 (file)
index c7e729e..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-from __future__ import unicode_literals
-
-import random
-import string
-from collections import defaultdict
-
-
-from django.test import TestCase, override_settings
-from django.test.html import parse_html
-
-from . import Thing
-from ..base import ModelLookup
-
-
-def parsed_inputs(html):
-    "Returns a dictionary mapping name --> node of inputs found in the HTML."
-    node = parse_html(html)
-    inputs = {}
-    for field in [c for c in node.children if c.name == 'input']:
-        name = dict(field.attributes)['name']
-        current = inputs.get(name, [])
-        current.append(field)
-        inputs[name] = current
-    return inputs
-
-
-@override_settings(ROOT_URLCONF='selectable.tests.urls')
-class BaseSelectableTestCase(TestCase):
-
-    def get_random_string(self, length=10):
-        return ''.join(random.choice(string.ascii_letters) for x in range(length))
-
-    def create_thing(self, data=None):
-        data = data or {}
-        defaults = {
-            'name': self.get_random_string(),
-            'description': self.get_random_string(),
-        }
-        defaults.update(data)
-        return Thing.objects.create(**defaults)
-
-
-class SimpleModelLookup(ModelLookup):
-    model = Thing
-    search_fields = ('name__icontains', )
-
-
-def parsed_widget_attributes(widget):
-    """
-    Get a dictionary-like object containing all HTML attributes
-    of the rendered widget.
-
-    Lookups on this object raise ValueError if there is more than one attribute
-    of the given name in the HTML, and they have different values.
-    """
-    # For the tests that use this, it generally doesn't matter what the value
-    # is, so we supply anything.
-    rendered = widget.render('a_name', 'a_value')
-    return AttrMap(rendered)
-
-
-class AttrMap(object):
-    def __init__(self, html):
-        dom = parse_html(html)
-        self._attrs = defaultdict(set)
-        self._build_attr_map(dom)
-
-    def _build_attr_map(self, dom):
-        for node in _walk_nodes(dom):
-            if node.attributes is not None:
-                for (k, v) in node.attributes:
-                    self._attrs[k].add(v)
-
-    def __contains__(self, key):
-        return key in self._attrs and len(self._attrs[key]) > 0
-
-    def __getitem__(self, key):
-        if key not in self:
-            raise KeyError(key)
-        vals = self._attrs[key]
-        if len(vals) > 1:
-            raise ValueError("More than one value for attribute {0}: {1}".
-                             format(key, ", ".join(vals)))
-        else:
-            return list(vals)[0]
-
-
-def _walk_nodes(dom):
-    yield dom
-    for child in dom.children:
-        for item in _walk_nodes(child):
-            yield item
diff --git a/dep/django-selectable/selectable/tests/qunit/helpers.js b/dep/django-selectable/selectable/tests/qunit/helpers.js
deleted file mode 100644 (file)
index 7bbb224..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-/* Test utility functions */
-(function ($) {
-
-    window.createTextComplete = function (name, attrs) {
-        var inputAttrs = {
-            'name': name,
-            'data-selectable-type': 'text',
-            'data-selectable-url': '/lookup/core-fruitlookup/',
-            'type': 'text'
-        }, finalAttrs = $.extend({}, inputAttrs, attrs || {});
-        return $('<input>', finalAttrs);
-    };
-
-    window.createTextCombobox = function (name, attrs) {
-        // Force change of the name and type
-        var inputAttrs = $.extend({
-            'data-selectable-type': 'combobox'
-        }, attrs || {});
-        return window.createTextComplete(name, inputAttrs);
-    };
-
-    window.createTextSelect = function (name, attrs) {
-        var inputAttrs = $.extend({
-            'name': name + '_0'
-        }, attrs || {}), textInput, hiddenInput,
-        hiddenAttrs = {
-            'name': name + '_1',
-            'data-selectable-type': 'hidden',
-            'type': 'hidden'
-        };
-        textInput = window.createTextComplete(name, inputAttrs);
-        hiddenInput = $('<input>', hiddenAttrs);
-        return [textInput, hiddenInput];
-    };
-
-    window.createComboboxSelect = function (name, attrs) {
-        var inputAttrs = $.extend({
-            'name': name + '_0'
-        }, attrs || {}), textInput, hiddenInput,
-        hiddenAttrs = {
-            'name': name + '_1',
-            'data-selectable-type': 'hidden',
-            'type': 'hidden'
-        };
-        textInput = window.createTextCombobox(name, inputAttrs);
-        hiddenInput = $('<input>', hiddenAttrs);
-        return [textInput, hiddenInput];
-    };
-
-    window.createTextSelectMultiple = function (name, attrs) {
-        var inputAttrs = $.extend({
-            'name': name + '_0',
-            'data-selectable-multiple': true,
-            'data-selectable-allow-new': false
-        }, attrs || {}), textInput, hiddenInput,
-        hiddenAttrs = {
-            'name': name + '_1',
-            'data-selectable-type': 'hidden-multiple',
-            'type': 'hidden'
-        };
-        textInput = window.createTextComplete(name, inputAttrs);
-        hiddenInput = $('<input>', hiddenAttrs);
-        return [textInput, hiddenInput];
-    };
-
-    window.createComboboxSelectMultiple = function (name, attrs) {
-        var inputAttrs = $.extend({
-            'name': name + '_0',
-            'data-selectable-multiple': true,
-            'data-selectable-allow-new': false
-        }, attrs || {}), textInput, hiddenInput,
-        hiddenAttrs = {
-            'name': name + '_1',
-            'data-selectable-type': 'hidden-multiple',
-            'type': 'hidden'
-        };
-        textInput = window.createTextCombobox(name, inputAttrs);
-        hiddenInput = $('<input>', hiddenAttrs);
-        return [textInput, hiddenInput];
-    };
-
-    window.simpleLookupResponse = function () {
-        var meta = {
-            "term": "ap",
-            "limit": 25,
-            "page": 1,
-            "more": "Show more results"
-        }, data = [
-            {"id": 1, "value": "Apple", "label": "Apple"},
-            {"id": 3, "value": "Grape", "label": "Grape"}
-        ];
-        return {"meta": meta, "data": data};
-    };
-
-    window.paginatedLookupResponse = function () {
-        var meta = {
-            "term": "ap",
-            "limit": 2,
-            "page": 1,
-            "more": "Show more results"
-        }, data = [
-            {"id": 1, "value": "Apple", "label": "Apple"},
-            {"id": 3, "value": "Grape", "label": "Grape"},
-            {"id": null, "page": 2, "label": "Show more results"}
-        ];
-        return {"meta": meta, "data": data};
-    };
-})(jQuery);
\ No newline at end of file
diff --git a/dep/django-selectable/selectable/tests/qunit/index.html b/dep/django-selectable/selectable/tests/qunit/index.html
deleted file mode 100644 (file)
index 496a6fc..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-    <meta charset="utf-8">
-    <title>Django Selectable Test Suite</title>
-    <link rel="stylesheet" href="http://code.jquery.com/qunit/qunit-1.11.0.css" media="screen">
-    <script src="jquery-loader.js"></script>
-    <script src="http://code.jquery.com/qunit/qunit-1.11.0.js"></script>
-    <script src="sinon-1.5.2.js"></script>
-    <script src="helpers.js"></script>
-    <script>QUnit.config.autostart = false;</script>
-    <script data-main="main" src="http://cdnjs.cloudflare.com/ajax/libs/require.js/2.1.4/require.min.js"></script>
-</head>
-<body>
-    <h1 id="qunit-header">Django Selectable Test Suite</h1>
-    <h2 id="qunit-banner"></h2>
-    <div id="qunit-testrunner-toolbar"></div>
-    <h2 id="qunit-userAgent"></h2>
-    <ol id="qunit-tests"></ol>
-    <div id="qunit-fixture"></div>
-</body>
-</html>
diff --git a/dep/django-selectable/selectable/tests/qunit/jquery-loader.js b/dep/django-selectable/selectable/tests/qunit/jquery-loader.js
deleted file mode 100644 (file)
index 84ee0a9..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-(function() {
-  // Get any jquery=___ param from the query string.
-  var jqversion = location.search.match(/[?&]jquery=(.*?)(?=&|$)/);
-  var uiversion = location.search.match(/[?&]ui=(.*?)(?=&|$)/);
-  var path;
-  window.jqversion = jqversion && jqversion[1] || '1.11.2';
-  window.uiversion = uiversion && uiversion[1] || '1.11.4';
-  jqpath = 'http://code.jquery.com/jquery-' + window.jqversion + '.js';
-  uipath = 'http://code.jquery.com/ui/' + window.uiversion + '/jquery-ui.js';
-  // This is the only time I'll ever use document.write, I promise!
-  document.write('<script src="' + jqpath + '"></script>');
-  document.write('<script src="' + uipath + '"></script>');
-}());
diff --git a/dep/django-selectable/selectable/tests/qunit/main.js b/dep/django-selectable/selectable/tests/qunit/main.js
deleted file mode 100644 (file)
index de19ed8..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-/*global require, QUnit*/
-
-require.config({
-    baseUrl: '../../static/selectable/js/',
-    paths: {
-        selectable: 'jquery.dj.selectable'
-    },
-    shim: {
-        selectable: {
-            exports: 'jQuery'
-        }
-    }
-});
-
-require(['test-methods.js', 'test-events.js', 'test-options.js'], function () {
-    //Tests loaded, run Tests
-    QUnit.load();
-    QUnit.start();
-});
\ No newline at end of file
diff --git a/dep/django-selectable/selectable/tests/qunit/sinon-1.5.2.js b/dep/django-selectable/selectable/tests/qunit/sinon-1.5.2.js
deleted file mode 100644 (file)
index 73f1435..0000000
+++ /dev/null
@@ -1,4153 +0,0 @@
-/**
- * Sinon.JS 1.5.2, 2012/11/27
- *
- * @author Christian Johansen ([email protected])
- * @author Contributors: https://github.com/cjohansen/Sinon.JS/blob/master/AUTHORS
- *
- * (The BSD License)
- *
- * Copyright (c) 2010-2012, Christian Johansen, [email protected]
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- *     * Redistributions of source code must retain the above copyright notice,
- *       this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright notice,
- *       this list of conditions and the following disclaimer in the documentation
- *       and/or other materials provided with the distribution.
- *     * Neither the name of Christian Johansen nor the names of his contributors
- *       may be used to endorse or promote products derived from this software
- *       without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-var sinon = (function () {
-"use strict";
-
-var buster = (function (setTimeout, B) {
-    var isNode = typeof require == "function" && typeof module == "object";
-    var div = typeof document != "undefined" && document.createElement("div");
-    var F = function () {};
-
-    var buster = {
-        bind: function bind(obj, methOrProp) {
-            var method = typeof methOrProp == "string" ? obj[methOrProp] : methOrProp;
-            var args = Array.prototype.slice.call(arguments, 2);
-            return function () {
-                var allArgs = args.concat(Array.prototype.slice.call(arguments));
-                return method.apply(obj, allArgs);
-            };
-        },
-
-        partial: function partial(fn) {
-            var args = [].slice.call(arguments, 1);
-            return function () {
-                return fn.apply(this, args.concat([].slice.call(arguments)));
-            };
-        },
-
-        create: function create(object) {
-            F.prototype = object;
-            return new F();
-        },
-
-        extend: function extend(target) {
-            if (!target) { return; }
-            for (var i = 1, l = arguments.length, prop; i < l; ++i) {
-                for (prop in arguments[i]) {
-                    target[prop] = arguments[i][prop];
-                }
-            }
-            return target;
-        },
-
-        nextTick: function nextTick(callback) {
-            if (typeof process != "undefined" && process.nextTick) {
-                return process.nextTick(callback);
-            }
-            setTimeout(callback, 0);
-        },
-
-        functionName: function functionName(func) {
-            if (!func) return "";
-            if (func.displayName) return func.displayName;
-            if (func.name) return func.name;
-            var matches = func.toString().match(/function\s+([^\(]+)/m);
-            return matches && matches[1] || "";
-        },
-
-        isNode: function isNode(obj) {
-            if (!div) return false;
-            try {
-                obj.appendChild(div);
-                obj.removeChild(div);
-            } catch (e) {
-                return false;
-            }
-            return true;
-        },
-
-        isElement: function isElement(obj) {
-            return obj && obj.nodeType === 1 && buster.isNode(obj);
-        },
-
-        isArray: function isArray(arr) {
-            return Object.prototype.toString.call(arr) == "[object Array]";
-        },
-
-        flatten: function flatten(arr) {
-            var result = [], arr = arr || [];
-            for (var i = 0, l = arr.length; i < l; ++i) {
-                result = result.concat(buster.isArray(arr[i]) ? flatten(arr[i]) : arr[i]);
-            }
-            return result;
-        },
-
-        each: function each(arr, callback) {
-            for (var i = 0, l = arr.length; i < l; ++i) {
-                callback(arr[i]);
-            }
-        },
-
-        map: function map(arr, callback) {
-            var results = [];
-            for (var i = 0, l = arr.length; i < l; ++i) {
-                results.push(callback(arr[i]));
-            }
-            return results;
-        },
-
-        parallel: function parallel(fns, callback) {
-            function cb(err, res) {
-                if (typeof callback == "function") {
-                    callback(err, res);
-                    callback = null;
-                }
-            }
-            if (fns.length == 0) { return cb(null, []); }
-            var remaining = fns.length, results = [];
-            function makeDone(num) {
-                return function done(err, result) {
-                    if (err) { return cb(err); }
-                    results[num] = result;
-                    if (--remaining == 0) { cb(null, results); }
-                };
-            }
-            for (var i = 0, l = fns.length; i < l; ++i) {
-                fns[i](makeDone(i));
-            }
-        },
-
-        series: function series(fns, callback) {
-            function cb(err, res) {
-                if (typeof callback == "function") {
-                    callback(err, res);
-                }
-            }
-            var remaining = fns.slice();
-            var results = [];
-            function callNext() {
-                if (remaining.length == 0) return cb(null, results);
-                var promise = remaining.shift()(next);
-                if (promise && typeof promise.then == "function") {
-                    promise.then(buster.partial(next, null), next);
-                }
-            }
-            function next(err, result) {
-                if (err) return cb(err);
-                results.push(result);
-                callNext();
-            }
-            callNext();
-        },
-
-        countdown: function countdown(num, done) {
-            return function () {
-                if (--num == 0) done();
-            };
-        }
-    };
-
-    if (typeof process === "object" &&
-        typeof require === "function" && typeof module === "object") {
-        var crypto = require("crypto");
-        var path = require("path");
-
-        buster.tmpFile = function (fileName) {
-            var hashed = crypto.createHash("sha1");
-            hashed.update(fileName);
-            var tmpfileName = hashed.digest("hex");
-
-            if (process.platform == "win32") {
-                return path.join(process.env["TEMP"], tmpfileName);
-            } else {
-                return path.join("/tmp", tmpfileName);
-            }
-        };
-    }
-
-    if (Array.prototype.some) {
-        buster.some = function (arr, fn, thisp) {
-            return arr.some(fn, thisp);
-        };
-    } else {
-        // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/some
-        buster.some = function (arr, fun, thisp) {
-                        if (arr == null) { throw new TypeError(); }
-            arr = Object(arr);
-            var len = arr.length >>> 0;
-            if (typeof fun !== "function") { throw new TypeError(); }
-
-            for (var i = 0; i < len; i++) {
-                if (arr.hasOwnProperty(i) && fun.call(thisp, arr[i], i, arr)) {
-                    return true;
-                }
-            }
-
-            return false;
-        };
-    }
-
-    if (Array.prototype.filter) {
-        buster.filter = function (arr, fn, thisp) {
-            return arr.filter(fn, thisp);
-        };
-    } else {
-        // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/filter
-        buster.filter = function (fn, thisp) {
-                        if (this == null) { throw new TypeError(); }
-
-            var t = Object(this);
-            var len = t.length >>> 0;
-            if (typeof fn != "function") { throw new TypeError(); }
-
-            var res = [];
-            for (var i = 0; i < len; i++) {
-                if (i in t) {
-                    var val = t[i]; // in case fun mutates this
-                    if (fn.call(thisp, val, i, t)) { res.push(val); }
-                }
-            }
-
-            return res;
-        };
-    }
-
-    if (isNode) {
-        module.exports = buster;
-        buster.eventEmitter = require("./buster-event-emitter");
-        Object.defineProperty(buster, "defineVersionGetter", {
-            get: function () {
-                return require("./define-version-getter");
-            }
-        });
-    }
-
-    return buster.extend(B || {}, buster);
-}(setTimeout, buster));
-if (typeof buster === "undefined") {
-    var buster = {};
-}
-
-if (typeof module === "object" && typeof require === "function") {
-    buster = require("buster-core");
-}
-
-buster.format = buster.format || {};
-buster.format.excludeConstructors = ["Object", /^.$/];
-buster.format.quoteStrings = true;
-
-buster.format.ascii = (function () {
-
-    var hasOwn = Object.prototype.hasOwnProperty;
-
-    var specialObjects = [];
-    if (typeof global != "undefined") {
-        specialObjects.push({ obj: global, value: "[object global]" });
-    }
-    if (typeof document != "undefined") {
-        specialObjects.push({ obj: document, value: "[object HTMLDocument]" });
-    }
-    if (typeof window != "undefined") {
-        specialObjects.push({ obj: window, value: "[object Window]" });
-    }
-
-    function keys(object) {
-        var k = Object.keys && Object.keys(object) || [];
-
-        if (k.length == 0) {
-            for (var prop in object) {
-                if (hasOwn.call(object, prop)) {
-                    k.push(prop);
-                }
-            }
-        }
-
-        return k.sort();
-    }
-
-    function isCircular(object, objects) {
-        if (typeof object != "object") {
-            return false;
-        }
-
-        for (var i = 0, l = objects.length; i < l; ++i) {
-            if (objects[i] === object) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    function ascii(object, processed, indent) {
-        if (typeof object == "string") {
-            var quote = typeof this.quoteStrings != "boolean" || this.quoteStrings;
-            return processed || quote ? '"' + object + '"' : object;
-        }
-
-        if (typeof object == "function" && !(object instanceof RegExp)) {
-            return ascii.func(object);
-        }
-
-        processed = processed || [];
-
-        if (isCircular(object, processed)) {
-            return "[Circular]";
-        }
-
-        if (Object.prototype.toString.call(object) == "[object Array]") {
-            return ascii.array.call(this, object, processed);
-        }
-
-        if (!object) {
-            return "" + object;
-        }
-
-        if (buster.isElement(object)) {
-            return ascii.element(object);
-        }
-
-        if (typeof object.toString == "function" &&
-            object.toString !== Object.prototype.toString) {
-            return object.toString();
-        }
-
-        for (var i = 0, l = specialObjects.length; i < l; i++) {
-            if (object === specialObjects[i].obj) {
-                return specialObjects[i].value;
-            }
-        }
-
-        return ascii.object.call(this, object, processed, indent);
-    }
-
-    ascii.func = function (func) {
-        return "function " + buster.functionName(func) + "() {}";
-    };
-
-    ascii.array = function (array, processed) {
-        processed = processed || [];
-        processed.push(array);
-        var pieces = [];
-
-        for (var i = 0, l = array.length; i < l; ++i) {
-            pieces.push(ascii.call(this, array[i], processed));
-        }
-
-        return "[" + pieces.join(", ") + "]";
-    };
-
-    ascii.object = function (object, processed, indent) {
-        processed = processed || [];
-        processed.push(object);
-        indent = indent || 0;
-        var pieces = [], properties = keys(object), prop, str, obj;
-        var is = "";
-        var length = 3;
-
-        for (var i = 0, l = indent; i < l; ++i) {
-            is += " ";
-        }
-
-        for (i = 0, l = properties.length; i < l; ++i) {
-            prop = properties[i];
-            obj = object[prop];
-
-            if (isCircular(obj, processed)) {
-                str = "[Circular]";
-            } else {
-                str = ascii.call(this, obj, processed, indent + 2);
-            }
-
-            str = (/\s/.test(prop) ? '"' + prop + '"' : prop) + ": " + str;
-            length += str.length;
-            pieces.push(str);
-        }
-
-        var cons = ascii.constructorName.call(this, object);
-        var prefix = cons ? "[" + cons + "] " : ""
-
-        return (length + indent) > 80 ?
-            prefix + "{\n  " + is + pieces.join(",\n  " + is) + "\n" + is + "}" :
-            prefix + "{ " + pieces.join(", ") + " }";
-    };
-
-    ascii.element = function (element) {
-        var tagName = element.tagName.toLowerCase();
-        var attrs = element.attributes, attribute, pairs = [], attrName;
-
-        for (var i = 0, l = attrs.length; i < l; ++i) {
-            attribute = attrs.item(i);
-            attrName = attribute.nodeName.toLowerCase().replace("html:", "");
-
-            if (attrName == "contenteditable" && attribute.nodeValue == "inherit") {
-                continue;
-            }
-
-            if (!!attribute.nodeValue) {
-                pairs.push(attrName + "=\"" + attribute.nodeValue + "\"");
-            }
-        }
-
-        var formatted = "<" + tagName + (pairs.length > 0 ? " " : "");
-        var content = element.innerHTML;
-
-        if (content.length > 20) {
-            content = content.substr(0, 20) + "[...]";
-        }
-
-        var res = formatted + pairs.join(" ") + ">" + content + "</" + tagName + ">";
-
-        return res.replace(/ contentEditable="inherit"/, "");
-    };
-
-    ascii.constructorName = function (object) {
-        var name = buster.functionName(object && object.constructor);
-        var excludes = this.excludeConstructors || buster.format.excludeConstructors || [];
-
-        for (var i = 0, l = excludes.length; i < l; ++i) {
-            if (typeof excludes[i] == "string" && excludes[i] == name) {
-                return "";
-            } else if (excludes[i].test && excludes[i].test(name)) {
-                return "";
-            }
-        }
-
-        return name;
-    };
-
-    return ascii;
-}());
-
-if (typeof module != "undefined") {
-    module.exports = buster.format;
-}
-/*jslint eqeqeq: false, onevar: false, forin: true, nomen: false, regexp: false, plusplus: false*/
-/*global module, require, __dirname, document*/
-/**
- * Sinon core utilities. For internal use only.
- *
- * @author Christian Johansen ([email protected])
- * @license BSD
- *
- * Copyright (c) 2010-2011 Christian Johansen
- */
-
-var sinon = (function (buster) {
-    var div = typeof document != "undefined" && document.createElement("div");
-    var hasOwn = Object.prototype.hasOwnProperty;
-
-    function isDOMNode(obj) {
-        var success = false;
-
-        try {
-            obj.appendChild(div);
-            success = div.parentNode == obj;
-        } catch (e) {
-            return false;
-        } finally {
-            try {
-                obj.removeChild(div);
-            } catch (e) {
-                // Remove failed, not much we can do about that
-            }
-        }
-
-        return success;
-    }
-
-    function isElement(obj) {
-        return div && obj && obj.nodeType === 1 && isDOMNode(obj);
-    }
-
-    function isFunction(obj) {
-        return typeof obj === "function" || !!(obj && obj.constructor && obj.call && obj.apply);
-    }
-
-    function mirrorProperties(target, source) {
-        for (var prop in source) {
-            if (!hasOwn.call(target, prop)) {
-                target[prop] = source[prop];
-            }
-        }
-    }
-
-    var sinon = {
-        wrapMethod: function wrapMethod(object, property, method) {
-            if (!object) {
-                throw new TypeError("Should wrap property of object");
-            }
-
-            if (typeof method != "function") {
-                throw new TypeError("Method wrapper should be function");
-            }
-
-            var wrappedMethod = object[property];
-
-            if (!isFunction(wrappedMethod)) {
-                throw new TypeError("Attempted to wrap " + (typeof wrappedMethod) + " property " +
-                                    property + " as function");
-            }
-
-            if (wrappedMethod.restore && wrappedMethod.restore.sinon) {
-                throw new TypeError("Attempted to wrap " + property + " which is already wrapped");
-            }
-
-            if (wrappedMethod.calledBefore) {
-                var verb = !!wrappedMethod.returns ? "stubbed" : "spied on";
-                throw new TypeError("Attempted to wrap " + property + " which is already " + verb);
-            }
-
-            // IE 8 does not support hasOwnProperty on the window object.
-            var owned = hasOwn.call(object, property);
-            object[property] = method;
-            method.displayName = property;
-
-            method.restore = function () {
-                // For prototype properties try to reset by delete first.
-                // If this fails (ex: localStorage on mobile safari) then force a reset
-                // via direct assignment.
-                if (!owned) {
-                    delete object[property];
-                }
-                if (object[property] === method) {
-                    object[property] = wrappedMethod;
-                }
-            };
-
-            method.restore.sinon = true;
-            mirrorProperties(method, wrappedMethod);
-
-            return method;
-        },
-
-        extend: function extend(target) {
-            for (var i = 1, l = arguments.length; i < l; i += 1) {
-                for (var prop in arguments[i]) {
-                    if (arguments[i].hasOwnProperty(prop)) {
-                        target[prop] = arguments[i][prop];
-                    }
-
-                    // DONT ENUM bug, only care about toString
-                    if (arguments[i].hasOwnProperty("toString") &&
-                        arguments[i].toString != target.toString) {
-                        target.toString = arguments[i].toString;
-                    }
-                }
-            }
-
-            return target;
-        },
-
-        create: function create(proto) {
-            var F = function () {};
-            F.prototype = proto;
-            return new F();
-        },
-
-        deepEqual: function deepEqual(a, b) {
-            if (sinon.match && sinon.match.isMatcher(a)) {
-                return a.test(b);
-            }
-            if (typeof a != "object" || typeof b != "object") {
-                return a === b;
-            }
-
-            if (isElement(a) || isElement(b)) {
-                return a === b;
-            }
-
-            if (a === b) {
-                return true;
-            }
-
-            if ((a === null && b !== null) || (a !== null && b === null)) {
-                return false;
-            }
-
-            var aString = Object.prototype.toString.call(a);
-            if (aString != Object.prototype.toString.call(b)) {
-                return false;
-            }
-
-            if (aString == "[object Array]") {
-                if (a.length !== b.length) {
-                    return false;
-                }
-
-                for (var i = 0, l = a.length; i < l; i += 1) {
-                    if (!deepEqual(a[i], b[i])) {
-                        return false;
-                    }
-                }
-
-                return true;
-            }
-
-            var prop, aLength = 0, bLength = 0;
-
-            for (prop in a) {
-                aLength += 1;
-
-                if (!deepEqual(a[prop], b[prop])) {
-                    return false;
-                }
-            }
-
-            for (prop in b) {
-                bLength += 1;
-            }
-
-            if (aLength != bLength) {
-                return false;
-            }
-
-            return true;
-        },
-
-        functionName: function functionName(func) {
-            var name = func.displayName || func.name;
-
-            // Use function decomposition as a last resort to get function
-            // name. Does not rely on function decomposition to work - if it
-            // doesn't debugging will be slightly less informative
-            // (i.e. toString will say 'spy' rather than 'myFunc').
-            if (!name) {
-                var matches = func.toString().match(/function ([^\s\(]+)/);
-                name = matches && matches[1];
-            }
-
-            return name;
-        },
-
-        functionToString: function toString() {
-            if (this.getCall && this.callCount) {
-                var thisValue, prop, i = this.callCount;
-
-                while (i--) {
-                    thisValue = this.getCall(i).thisValue;
-
-                    for (prop in thisValue) {
-                        if (thisValue[prop] === this) {
-                            return prop;
-                        }
-                    }
-                }
-            }
-
-            return this.displayName || "sinon fake";
-        },
-
-        getConfig: function (custom) {
-            var config = {};
-            custom = custom || {};
-            var defaults = sinon.defaultConfig;
-
-            for (var prop in defaults) {
-                if (defaults.hasOwnProperty(prop)) {
-                    config[prop] = custom.hasOwnProperty(prop) ? custom[prop] : defaults[prop];
-                }
-            }
-
-            return config;
-        },
-
-        format: function (val) {
-            return "" + val;
-        },
-
-        defaultConfig: {
-            injectIntoThis: true,
-            injectInto: null,
-            properties: ["spy", "stub", "mock", "clock", "server", "requests"],
-            useFakeTimers: true,
-            useFakeServer: true
-        },
-
-        timesInWords: function timesInWords(count) {
-            return count == 1 && "once" ||
-                count == 2 && "twice" ||
-                count == 3 && "thrice" ||
-                (count || 0) + " times";
-        },
-
-        calledInOrder: function (spies) {
-            for (var i = 1, l = spies.length; i < l; i++) {
-                if (!spies[i - 1].calledBefore(spies[i])) {
-                    return false;
-                }
-            }
-
-            return true;
-        },
-
-        orderByFirstCall: function (spies) {
-            return spies.sort(function (a, b) {
-                // uuid, won't ever be equal
-                var aCall = a.getCall(0);
-                var bCall = b.getCall(0);
-                var aId = aCall && aCall.callId || -1;
-                var bId = bCall && bCall.callId || -1;
-
-                return aId < bId ? -1 : 1;
-            });
-        },
-
-        log: function () {},
-
-        logError: function (label, err) {
-            var msg = label + " threw exception: "
-            sinon.log(msg + "[" + err.name + "] " + err.message);
-            if (err.stack) { sinon.log(err.stack); }
-
-            setTimeout(function () {
-                err.message = msg + err.message;
-                throw err;
-            }, 0);
-        },
-
-        typeOf: function (value) {
-            if (value === null) {
-                return "null";
-            }
-            else if (value === undefined) {
-                return "undefined";
-            }
-            var string = Object.prototype.toString.call(value);
-            return string.substring(8, string.length - 1).toLowerCase();
-        }
-    };
-
-    var isNode = typeof module == "object" && typeof require == "function";
-
-    if (isNode) {
-        try {
-            buster = { format: require("buster-format") };
-        } catch (e) {}
-        module.exports = sinon;
-        module.exports.spy = require("./sinon/spy");
-        module.exports.stub = require("./sinon/stub");
-        module.exports.mock = require("./sinon/mock");
-        module.exports.collection = require("./sinon/collection");
-        module.exports.assert = require("./sinon/assert");
-        module.exports.sandbox = require("./sinon/sandbox");
-        module.exports.test = require("./sinon/test");
-        module.exports.testCase = require("./sinon/test_case");
-        module.exports.assert = require("./sinon/assert");
-        module.exports.match = require("./sinon/match");
-    }
-
-    if (buster) {
-        var formatter = sinon.create(buster.format);
-        formatter.quoteStrings = false;
-        sinon.format = function () {
-            return formatter.ascii.apply(formatter, arguments);
-        };
-    } else if (isNode) {
-        try {
-            var util = require("util");
-            sinon.format = function (value) {
-                return typeof value == "object" && value.toString === Object.prototype.toString ? util.inspect(value) : value;
-            };
-        } catch (e) {
-            /* Node, but no util module - would be very old, but better safe than
-             sorry */
-        }
-    }
-
-    return sinon;
-}(typeof buster == "object" && buster));
-
-/* @depend ../sinon.js */
-/*jslint eqeqeq: false, onevar: false, plusplus: false*/
-/*global module, require, sinon*/
-/**
- * Match functions
- *
- * @author Maximilian Antoni ([email protected])
- * @license BSD
- *
- * Copyright (c) 2012 Maximilian Antoni
- */
-
-(function (sinon) {
-    var commonJSModule = typeof module == "object" && typeof require == "function";
-
-    if (!sinon && commonJSModule) {
-        sinon = require("../sinon");
-    }
-
-    if (!sinon) {
-        return;
-    }
-
-    function assertType(value, type, name) {
-        var actual = sinon.typeOf(value);
-        if (actual !== type) {
-            throw new TypeError("Expected type of " + name + " to be " +
-                type + ", but was " + actual);
-        }
-    }
-
-    var matcher = {
-        toString: function () {
-            return this.message;
-        }
-    };
-
-    function isMatcher(object) {
-        return matcher.isPrototypeOf(object);
-    }
-
-    function matchObject(expectation, actual) {
-        if (actual === null || actual === undefined) {
-            return false;
-        }
-        for (var key in expectation) {
-            if (expectation.hasOwnProperty(key)) {
-                var exp = expectation[key];
-                var act = actual[key];
-                if (match.isMatcher(exp)) {
-                    if (!exp.test(act)) {
-                        return false;
-                    }
-                } else if (sinon.typeOf(exp) === "object") {
-                    if (!matchObject(exp, act)) {
-                        return false;
-                    }
-                } else if (!sinon.deepEqual(exp, act)) {
-                    return false;
-                }
-            }
-        }
-        return true;
-    }
-
-    matcher.or = function (m2) {
-        if (!isMatcher(m2)) {
-            throw new TypeError("Matcher expected");
-        }
-        var m1 = this;
-        var or = sinon.create(matcher);
-        or.test = function (actual) {
-            return m1.test(actual) || m2.test(actual);
-        };
-        or.message = m1.message + ".or(" + m2.message + ")";
-        return or;
-    };
-
-    matcher.and = function (m2) {
-        if (!isMatcher(m2)) {
-            throw new TypeError("Matcher expected");
-        }
-        var m1 = this;
-        var and = sinon.create(matcher);
-        and.test = function (actual) {
-            return m1.test(actual) && m2.test(actual);
-        };
-        and.message = m1.message + ".and(" + m2.message + ")";
-        return and;
-    };
-
-    var match = function (expectation, message) {
-        var m = sinon.create(matcher);
-        var type = sinon.typeOf(expectation);
-        switch (type) {
-        case "object":
-            if (typeof expectation.test === "function") {
-                m.test = function (actual) {
-                    return expectation.test(actual) === true;
-                };
-                m.message = "match(" + sinon.functionName(expectation.test) + ")";
-                return m;
-            }
-            var str = [];
-            for (var key in expectation) {
-                if (expectation.hasOwnProperty(key)) {
-                    str.push(key + ": " + expectation[key]);
-                }
-            }
-            m.test = function (actual) {
-                return matchObject(expectation, actual);
-            };
-            m.message = "match(" + str.join(", ") + ")";
-            break;
-        case "number":
-            m.test = function (actual) {
-                return expectation == actual;
-            };
-            break;
-        case "string":
-            m.test = function (actual) {
-                if (typeof actual !== "string") {
-                    return false;
-                }
-                return actual.indexOf(expectation) !== -1;
-            };
-            m.message = "match(\"" + expectation + "\")";
-            break;
-        case "regexp":
-            m.test = function (actual) {
-                if (typeof actual !== "string") {
-                    return false;
-                }
-                return expectation.test(actual);
-            };
-            break;
-        case "function":
-            m.test = expectation;
-            if (message) {
-                m.message = message;
-            } else {
-                m.message = "match(" + sinon.functionName(expectation) + ")";
-            }
-            break;
-        default:
-            m.test = function (actual) {
-              return sinon.deepEqual(expectation, actual);
-            };
-        }
-        if (!m.message) {
-            m.message = "match(" + expectation + ")";
-        }
-        return m;
-    };
-
-    match.isMatcher = isMatcher;
-
-    match.any = match(function () {
-        return true;
-    }, "any");
-
-    match.defined = match(function (actual) {
-        return actual !== null && actual !== undefined;
-    }, "defined");
-
-    match.truthy = match(function (actual) {
-        return !!actual;
-    }, "truthy");
-
-    match.falsy = match(function (actual) {
-        return !actual;
-    }, "falsy");
-
-    match.same = function (expectation) {
-        return match(function (actual) {
-            return expectation === actual;
-        }, "same(" + expectation + ")");
-    };
-
-    match.typeOf = function (type) {
-        assertType(type, "string", "type");
-        return match(function (actual) {
-            return sinon.typeOf(actual) === type;
-        }, "typeOf(\"" + type + "\")");
-    };
-
-    match.instanceOf = function (type) {
-        assertType(type, "function", "type");
-        return match(function (actual) {
-            return actual instanceof type;
-        }, "instanceOf(" + sinon.functionName(type) + ")");
-    };
-
-    function createPropertyMatcher(propertyTest, messagePrefix) {
-        return function (property, value) {
-            assertType(property, "string", "property");
-            var onlyProperty = arguments.length === 1;
-            var message = messagePrefix + "(\"" + property + "\"";
-            if (!onlyProperty) {
-                message += ", " + value;
-            }
-            message += ")";
-            return match(function (actual) {
-                if (actual === undefined || actual === null ||
-                        !propertyTest(actual, property)) {
-                    return false;
-                }
-                return onlyProperty || sinon.deepEqual(value, actual[property]);
-            }, message);
-        };
-    }
-
-    match.has = createPropertyMatcher(function (actual, property) {
-        if (typeof actual === "object") {
-            return property in actual;
-        }
-        return actual[property] !== undefined;
-    }, "has");
-
-    match.hasOwn = createPropertyMatcher(function (actual, property) {
-        return actual.hasOwnProperty(property);
-    }, "hasOwn");
-
-    match.bool = match.typeOf("boolean");
-    match.number = match.typeOf("number");
-    match.string = match.typeOf("string");
-    match.object = match.typeOf("object");
-    match.func = match.typeOf("function");
-    match.array = match.typeOf("array");
-    match.regexp = match.typeOf("regexp");
-    match.date = match.typeOf("date");
-
-    if (commonJSModule) {
-        module.exports = match;
-    } else {
-        sinon.match = match;
-    }
-}(typeof sinon == "object" && sinon || null));
-
-/**
- * @depend ../sinon.js
- * @depend match.js
- */
-/*jslint eqeqeq: false, onevar: false, plusplus: false*/
-/*global module, require, sinon*/
-/**
- * Spy functions
- *
- * @author Christian Johansen ([email protected])
- * @license BSD
- *
- * Copyright (c) 2010-2011 Christian Johansen
- */
-
-(function (sinon) {
-    var commonJSModule = typeof module == "object" && typeof require == "function";
-    var spyCall;
-    var callId = 0;
-    var push = [].push;
-    var slice = Array.prototype.slice;
-
-    if (!sinon && commonJSModule) {
-        sinon = require("../sinon");
-    }
-
-    if (!sinon) {
-        return;
-    }
-
-    function spy(object, property) {
-        if (!property && typeof object == "function") {
-            return spy.create(object);
-        }
-
-        if (!object && !property) {
-            return spy.create(function () {});
-        }
-
-        var method = object[property];
-        return sinon.wrapMethod(object, property, spy.create(method));
-    }
-
-    sinon.extend(spy, (function () {
-
-        function delegateToCalls(api, method, matchAny, actual, notCalled) {
-            api[method] = function () {
-                if (!this.called) {
-                    if (notCalled) {
-                        return notCalled.apply(this, arguments);
-                    }
-                    return false;
-                }
-
-                var currentCall;
-                var matches = 0;
-
-                for (var i = 0, l = this.callCount; i < l; i += 1) {
-                    currentCall = this.getCall(i);
-
-                    if (currentCall[actual || method].apply(currentCall, arguments)) {
-                        matches += 1;
-
-                        if (matchAny) {
-                            return true;
-                        }
-                    }
-                }
-
-                return matches === this.callCount;
-            };
-        }
-
-        function matchingFake(fakes, args, strict) {
-            if (!fakes) {
-                return;
-            }
-
-            var alen = args.length;
-
-            for (var i = 0, l = fakes.length; i < l; i++) {
-                if (fakes[i].matches(args, strict)) {
-                    return fakes[i];
-                }
-            }
-        }
-
-        function incrementCallCount() {
-            this.called = true;
-            this.callCount += 1;
-            this.notCalled = false;
-            this.calledOnce = this.callCount == 1;
-            this.calledTwice = this.callCount == 2;
-            this.calledThrice = this.callCount == 3;
-        }
-
-        function createCallProperties() {
-            this.firstCall = this.getCall(0);
-            this.secondCall = this.getCall(1);
-            this.thirdCall = this.getCall(2);
-            this.lastCall = this.getCall(this.callCount - 1);
-        }
-
-        var vars = "a,b,c,d,e,f,g,h,i,j,k,l";
-        function createProxy(func) {
-            // Retain the function length:
-            var p;
-            if (func.length) {
-                eval("p = (function proxy(" + vars.substring(0, func.length * 2 - 1) +
-                  ") { return p.invoke(func, this, slice.call(arguments)); });");
-            }
-            else {
-                p = function proxy() {
-                    return p.invoke(func, this, slice.call(arguments));
-                };
-            }
-            return p;
-        }
-
-        var uuid = 0;
-
-        // Public API
-        var spyApi = {
-            reset: function () {
-                this.called = false;
-                this.notCalled = true;
-                this.calledOnce = false;
-                this.calledTwice = false;
-                this.calledThrice = false;
-                this.callCount = 0;
-                this.firstCall = null;
-                this.secondCall = null;
-                this.thirdCall = null;
-                this.lastCall = null;
-                this.args = [];
-                this.returnValues = [];
-                this.thisValues = [];
-                this.exceptions = [];
-                this.callIds = [];
-                if (this.fakes) {
-                    for (var i = 0; i < this.fakes.length; i++) {
-                        this.fakes[i].reset();
-                    }
-                }
-            },
-
-            create: function create(func) {
-                var name;
-
-                if (typeof func != "function") {
-                    func = function () {};
-                } else {
-                    name = sinon.functionName(func);
-                }
-
-                var proxy = createProxy(func);
-
-                sinon.extend(proxy, spy);
-                delete proxy.create;
-                sinon.extend(proxy, func);
-
-                proxy.reset();
-                proxy.prototype = func.prototype;
-                proxy.displayName = name || "spy";
-                proxy.toString = sinon.functionToString;
-                proxy._create = sinon.spy.create;
-                proxy.id = "spy#" + uuid++;
-
-                return proxy;
-            },
-
-            invoke: function invoke(func, thisValue, args) {
-                var matching = matchingFake(this.fakes, args);
-                var exception, returnValue;
-
-                incrementCallCount.call(this);
-                push.call(this.thisValues, thisValue);
-                push.call(this.args, args);
-                push.call(this.callIds, callId++);
-
-                try {
-                    if (matching) {
-                        returnValue = matching.invoke(func, thisValue, args);
-                    } else {
-                        returnValue = (this.func || func).apply(thisValue, args);
-                    }
-                } catch (e) {
-                    push.call(this.returnValues, undefined);
-                    exception = e;
-                    throw e;
-                } finally {
-                    push.call(this.exceptions, exception);
-                }
-
-                push.call(this.returnValues, returnValue);
-
-                createCallProperties.call(this);
-
-                return returnValue;
-            },
-
-            getCall: function getCall(i) {
-                if (i < 0 || i >= this.callCount) {
-                    return null;
-                }
-
-                return spyCall.create(this, this.thisValues[i], this.args[i],
-                                      this.returnValues[i], this.exceptions[i],
-                                      this.callIds[i]);
-            },
-
-            calledBefore: function calledBefore(spyFn) {
-                if (!this.called) {
-                    return false;
-                }
-
-                if (!spyFn.called) {
-                    return true;
-                }
-
-                return this.callIds[0] < spyFn.callIds[spyFn.callIds.length - 1];
-            },
-
-            calledAfter: function calledAfter(spyFn) {
-                if (!this.called || !spyFn.called) {
-                    return false;
-                }
-
-                return this.callIds[this.callCount - 1] > spyFn.callIds[spyFn.callCount - 1];
-            },
-
-            withArgs: function () {
-                var args = slice.call(arguments);
-
-                if (this.fakes) {
-                    var match = matchingFake(this.fakes, args, true);
-
-                    if (match) {
-                        return match;
-                    }
-                } else {
-                    this.fakes = [];
-                }
-
-                var original = this;
-                var fake = this._create();
-                fake.matchingAguments = args;
-                push.call(this.fakes, fake);
-
-                fake.withArgs = function () {
-                    return original.withArgs.apply(original, arguments);
-                };
-
-                for (var i = 0; i < this.args.length; i++) {
-                    if (fake.matches(this.args[i])) {
-                        incrementCallCount.call(fake);
-                        push.call(fake.thisValues, this.thisValues[i]);
-                        push.call(fake.args, this.args[i]);
-                        push.call(fake.returnValues, this.returnValues[i]);
-                        push.call(fake.exceptions, this.exceptions[i]);
-                        push.call(fake.callIds, this.callIds[i]);
-                    }
-                }
-                createCallProperties.call(fake);
-
-                return fake;
-            },
-
-            matches: function (args, strict) {
-                var margs = this.matchingAguments;
-
-                if (margs.length <= args.length &&
-                    sinon.deepEqual(margs, args.slice(0, margs.length))) {
-                    return !strict || margs.length == args.length;
-                }
-            },
-
-            printf: function (format) {
-                var spy = this;
-                var args = slice.call(arguments, 1);
-                var formatter;
-
-                return (format || "").replace(/%(.)/g, function (match, specifyer) {
-                    formatter = spyApi.formatters[specifyer];
-
-                    if (typeof formatter == "function") {
-                        return formatter.call(null, spy, args);
-                    } else if (!isNaN(parseInt(specifyer), 10)) {
-                        return sinon.format(args[specifyer - 1]);
-                    }
-
-                    return "%" + specifyer;
-                });
-            }
-        };
-
-        delegateToCalls(spyApi, "calledOn", true);
-        delegateToCalls(spyApi, "alwaysCalledOn", false, "calledOn");
-        delegateToCalls(spyApi, "calledWith", true);
-        delegateToCalls(spyApi, "calledWithMatch", true);
-        delegateToCalls(spyApi, "alwaysCalledWith", false, "calledWith");
-        delegateToCalls(spyApi, "alwaysCalledWithMatch", false, "calledWithMatch");
-        delegateToCalls(spyApi, "calledWithExactly", true);
-        delegateToCalls(spyApi, "alwaysCalledWithExactly", false, "calledWithExactly");
-        delegateToCalls(spyApi, "neverCalledWith", false, "notCalledWith",
-            function () { return true; });
-        delegateToCalls(spyApi, "neverCalledWithMatch", false, "notCalledWithMatch",
-            function () { return true; });
-        delegateToCalls(spyApi, "threw", true);
-        delegateToCalls(spyApi, "alwaysThrew", false, "threw");
-        delegateToCalls(spyApi, "returned", true);
-        delegateToCalls(spyApi, "alwaysReturned", false, "returned");
-        delegateToCalls(spyApi, "calledWithNew", true);
-        delegateToCalls(spyApi, "alwaysCalledWithNew", false, "calledWithNew");
-        delegateToCalls(spyApi, "callArg", false, "callArgWith", function () {
-            throw new Error(this.toString() + " cannot call arg since it was not yet invoked.");
-        });
-        spyApi.callArgWith = spyApi.callArg;
-        delegateToCalls(spyApi, "yield", false, "yield", function () {
-            throw new Error(this.toString() + " cannot yield since it was not yet invoked.");
-        });
-        // "invokeCallback" is an alias for "yield" since "yield" is invalid in strict mode.
-        spyApi.invokeCallback = spyApi.yield;
-        delegateToCalls(spyApi, "yieldTo", false, "yieldTo", function (property) {
-            throw new Error(this.toString() + " cannot yield to '" + property +
-                "' since it was not yet invoked.");
-        });
-
-        spyApi.formatters = {
-            "c": function (spy) {
-                return sinon.timesInWords(spy.callCount);
-            },
-
-            "n": function (spy) {
-                return spy.toString();
-            },
-
-            "C": function (spy) {
-                var calls = [];
-
-                for (var i = 0, l = spy.callCount; i < l; ++i) {
-                    push.call(calls, "    " + spy.getCall(i).toString());
-                }
-
-                return calls.length > 0 ? "\n" + calls.join("\n") : "";
-            },
-
-            "t": function (spy) {
-                var objects = [];
-
-                for (var i = 0, l = spy.callCount; i < l; ++i) {
-                    push.call(objects, sinon.format(spy.thisValues[i]));
-                }
-
-                return objects.join(", ");
-            },
-
-            "*": function (spy, args) {
-                var formatted = [];
-
-                for (var i = 0, l = args.length; i < l; ++i) {
-                    push.call(formatted, sinon.format(args[i]));
-                }
-
-                return formatted.join(", ");
-            }
-        };
-
-        return spyApi;
-    }()));
-
-    spyCall = (function () {
-
-        function throwYieldError(proxy, text, args) {
-            var msg = sinon.functionName(proxy) + text;
-            if (args.length) {
-                msg += " Received [" + slice.call(args).join(", ") + "]";
-            }
-            throw new Error(msg);
-        }
-
-        var callApi = {
-            create: function create(spy, thisValue, args, returnValue, exception, id) {
-                var proxyCall = sinon.create(spyCall);
-                delete proxyCall.create;
-                proxyCall.proxy = spy;
-                proxyCall.thisValue = thisValue;
-                proxyCall.args = args;
-                proxyCall.returnValue = returnValue;
-                proxyCall.exception = exception;
-                proxyCall.callId = typeof id == "number" && id || callId++;
-
-                return proxyCall;
-            },
-
-            calledOn: function calledOn(thisValue) {
-                if (sinon.match && sinon.match.isMatcher(thisValue)) {
-                    return thisValue.test(this.thisValue);
-                }
-                return this.thisValue === thisValue;
-            },
-
-            calledWith: function calledWith() {
-                for (var i = 0, l = arguments.length; i < l; i += 1) {
-                    if (!sinon.deepEqual(arguments[i], this.args[i])) {
-                        return false;
-                    }
-                }
-
-                return true;
-            },
-
-            calledWithMatch: function calledWithMatch() {
-              for (var i = 0, l = arguments.length; i < l; i += 1) {
-                  var actual = this.args[i];
-                  var expectation = arguments[i];
-                  if (!sinon.match || !sinon.match(expectation).test(actual)) {
-                      return false;
-                  }
-              }
-              return true;
-            },
-
-            calledWithExactly: function calledWithExactly() {
-                return arguments.length == this.args.length &&
-                    this.calledWith.apply(this, arguments);
-            },
-
-            notCalledWith: function notCalledWith() {
-                return !this.calledWith.apply(this, arguments);
-            },
-
-            notCalledWithMatch: function notCalledWithMatch() {
-              return !this.calledWithMatch.apply(this, arguments);
-            },
-
-            returned: function returned(value) {
-                return sinon.deepEqual(value, this.returnValue);
-            },
-
-            threw: function threw(error) {
-                if (typeof error == "undefined" || !this.exception) {
-                    return !!this.exception;
-                }
-
-                if (typeof error == "string") {
-                    return this.exception.name == error;
-                }
-
-                return this.exception === error;
-            },
-
-            calledWithNew: function calledWithNew(thisValue) {
-                return this.thisValue instanceof this.proxy;
-            },
-
-            calledBefore: function (other) {
-                return this.callId < other.callId;
-            },
-
-            calledAfter: function (other) {
-                return this.callId > other.callId;
-            },
-
-            callArg: function (pos) {
-                this.args[pos]();
-            },
-
-            callArgWith: function (pos) {
-                var args = slice.call(arguments, 1);
-                this.args[pos].apply(null, args);
-            },
-
-            "yield": function () {
-                var args = this.args;
-                for (var i = 0, l = args.length; i < l; ++i) {
-                    if (typeof args[i] === "function") {
-                        args[i].apply(null, slice.call(arguments));
-                        return;
-                    }
-                }
-                throwYieldError(this.proxy, " cannot yield since no callback was passed.", args);
-            },
-
-            yieldTo: function (prop) {
-                var args = this.args;
-                for (var i = 0, l = args.length; i < l; ++i) {
-                    if (args[i] && typeof args[i][prop] === "function") {
-                        args[i][prop].apply(null, slice.call(arguments, 1));
-                        return;
-                    }
-                }
-                throwYieldError(this.proxy, " cannot yield to '" + prop +
-                    "' since no callback was passed.", args);
-            },
-
-            toString: function () {
-                var callStr = this.proxy.toString() + "(";
-                var args = [];
-
-                for (var i = 0, l = this.args.length; i < l; ++i) {
-                    push.call(args, sinon.format(this.args[i]));
-                }
-
-                callStr = callStr + args.join(", ") + ")";
-
-                if (typeof this.returnValue != "undefined") {
-                    callStr += " => " + sinon.format(this.returnValue);
-                }
-
-                if (this.exception) {
-                    callStr += " !" + this.exception.name;
-
-                    if (this.exception.message) {
-                        callStr += "(" + this.exception.message + ")";
-                    }
-                }
-
-                return callStr;
-            }
-        };
-        callApi.invokeCallback = callApi.yield;
-        return callApi;
-    }());
-
-    spy.spyCall = spyCall;
-
-    // This steps outside the module sandbox and will be removed
-    sinon.spyCall = spyCall;
-
-    if (commonJSModule) {
-        module.exports = spy;
-    } else {
-        sinon.spy = spy;
-    }
-}(typeof sinon == "object" && sinon || null));
-
-/**
- * @depend ../sinon.js
- * @depend spy.js
- */
-/*jslint eqeqeq: false, onevar: false*/
-/*global module, require, sinon*/
-/**
- * Stub functions
- *
- * @author Christian Johansen ([email protected])
- * @license BSD
- *
- * Copyright (c) 2010-2011 Christian Johansen
- */
-
-(function (sinon) {
-    var commonJSModule = typeof module == "object" && typeof require == "function";
-
-    if (!sinon && commonJSModule) {
-        sinon = require("../sinon");
-    }
-
-    if (!sinon) {
-        return;
-    }
-
-    function stub(object, property, func) {
-        if (!!func && typeof func != "function") {
-            throw new TypeError("Custom stub should be function");
-        }
-
-        var wrapper;
-
-        if (func) {
-            wrapper = sinon.spy && sinon.spy.create ? sinon.spy.create(func) : func;
-        } else {
-            wrapper = stub.create();
-        }
-
-        if (!object && !property) {
-            return sinon.stub.create();
-        }
-
-        if (!property && !!object && typeof object == "object") {
-            for (var prop in object) {
-                if (typeof object[prop] === "function") {
-                    stub(object, prop);
-                }
-            }
-
-            return object;
-        }
-
-        return sinon.wrapMethod(object, property, wrapper);
-    }
-
-    function getChangingValue(stub, property) {
-        var index = stub.callCount - 1;
-        var prop = index in stub[property] ? stub[property][index] : stub[property + "Last"];
-        stub[property + "Last"] = prop;
-
-        return prop;
-    }
-
-    function getCallback(stub, args) {
-        var callArgAt = getChangingValue(stub, "callArgAts");
-
-        if (callArgAt < 0) {
-            var callArgProp = getChangingValue(stub, "callArgProps");
-
-            for (var i = 0, l = args.length; i < l; ++i) {
-                if (!callArgProp && typeof args[i] == "function") {
-                    return args[i];
-                }
-
-                if (callArgProp && args[i] &&
-                    typeof args[i][callArgProp] == "function") {
-                    return args[i][callArgProp];
-                }
-            }
-
-            return null;
-        }
-
-        return args[callArgAt];
-    }
-
-    var join = Array.prototype.join;
-
-    function getCallbackError(stub, func, args) {
-        if (stub.callArgAtsLast < 0) {
-            var msg;
-
-            if (stub.callArgPropsLast) {
-                msg = sinon.functionName(stub) +
-                    " expected to yield to '" + stub.callArgPropsLast +
-                    "', but no object with such a property was passed."
-            } else {
-                msg = sinon.functionName(stub) +
-                            " expected to yield, but no callback was passed."
-            }
-
-            if (args.length > 0) {
-                msg += " Received [" + join.call(args, ", ") + "]";
-            }
-
-            return msg;
-        }
-
-        return "argument at index " + stub.callArgAtsLast + " is not a function: " + func;
-    }
-
-    var nextTick = (function () {
-        if (typeof process === "object" && typeof process.nextTick === "function") {
-            return process.nextTick;
-        } else if (typeof msSetImmediate === "function") {
-            return msSetImmediate.bind(window);
-        } else if (typeof setImmediate === "function") {
-            return setImmediate;
-        } else {
-            return function (callback) {
-                setTimeout(callback, 0);
-            };
-        }
-    })();
-
-    function callCallback(stub, args) {
-        if (stub.callArgAts.length > 0) {
-            var func = getCallback(stub, args);
-
-            if (typeof func != "function") {
-                throw new TypeError(getCallbackError(stub, func, args));
-            }
-
-            var index = stub.callCount - 1;
-
-            var callbackArguments = getChangingValue(stub, "callbackArguments");
-            var callbackContext = getChangingValue(stub, "callbackContexts");
-
-            if (stub.callbackAsync) {
-                nextTick(function() {
-                    func.apply(callbackContext, callbackArguments);
-                });
-            } else {
-                func.apply(callbackContext, callbackArguments);
-            }
-        }
-    }
-
-    var uuid = 0;
-
-    sinon.extend(stub, (function () {
-        var slice = Array.prototype.slice, proto;
-
-        function throwsException(error, message) {
-            if (typeof error == "string") {
-                this.exception = new Error(message || "");
-                this.exception.name = error;
-            } else if (!error) {
-                this.exception = new Error("Error");
-            } else {
-                this.exception = error;
-            }
-
-            return this;
-        }
-
-        proto = {
-            create: function create() {
-                var functionStub = function () {
-
-                    callCallback(functionStub, arguments);
-
-                    if (functionStub.exception) {
-                        throw functionStub.exception;
-                    } else if (typeof functionStub.returnArgAt == 'number') {
-                        return arguments[functionStub.returnArgAt];
-                    } else if (functionStub.returnThis) {
-                        return this;
-                    }
-                    return functionStub.returnValue;
-                };
-
-                functionStub.id = "stub#" + uuid++;
-                var orig = functionStub;
-                functionStub = sinon.spy.create(functionStub);
-                functionStub.func = orig;
-
-                functionStub.callArgAts = [];
-                functionStub.callbackArguments = [];
-                functionStub.callbackContexts = [];
-                functionStub.callArgProps = [];
-
-                sinon.extend(functionStub, stub);
-                functionStub._create = sinon.stub.create;
-                functionStub.displayName = "stub";
-                functionStub.toString = sinon.functionToString;
-
-                return functionStub;
-            },
-
-            returns: function returns(value) {
-                this.returnValue = value;
-
-                return this;
-            },
-
-            returnsArg: function returnsArg(pos) {
-                if (typeof pos != "number") {
-                    throw new TypeError("argument index is not number");
-                }
-
-                this.returnArgAt = pos;
-
-                return this;
-            },
-
-            returnsThis: function returnsThis() {
-                this.returnThis = true;
-
-                return this;
-            },
-
-            "throws": throwsException,
-            throwsException: throwsException,
-
-            callsArg: function callsArg(pos) {
-                if (typeof pos != "number") {
-                    throw new TypeError("argument index is not number");
-                }
-
-                this.callArgAts.push(pos);
-                this.callbackArguments.push([]);
-                this.callbackContexts.push(undefined);
-                this.callArgProps.push(undefined);
-
-                return this;
-            },
-
-            callsArgOn: function callsArgOn(pos, context) {
-                if (typeof pos != "number") {
-                    throw new TypeError("argument index is not number");
-                }
-                if (typeof context != "object") {
-                    throw new TypeError("argument context is not an object");
-                }
-
-                this.callArgAts.push(pos);
-                this.callbackArguments.push([]);
-                this.callbackContexts.push(context);
-                this.callArgProps.push(undefined);
-
-                return this;
-            },
-
-            callsArgWith: function callsArgWith(pos) {
-                if (typeof pos != "number") {
-                    throw new TypeError("argument index is not number");
-                }
-
-                this.callArgAts.push(pos);
-                this.callbackArguments.push(slice.call(arguments, 1));
-                this.callbackContexts.push(undefined);
-                this.callArgProps.push(undefined);
-
-                return this;
-            },
-
-            callsArgOnWith: function callsArgWith(pos, context) {
-                if (typeof pos != "number") {
-                    throw new TypeError("argument index is not number");
-                }
-                if (typeof context != "object") {
-                    throw new TypeError("argument context is not an object");
-                }
-
-                this.callArgAts.push(pos);
-                this.callbackArguments.push(slice.call(arguments, 2));
-                this.callbackContexts.push(context);
-                this.callArgProps.push(undefined);
-
-                return this;
-            },
-
-            yields: function () {
-                this.callArgAts.push(-1);
-                this.callbackArguments.push(slice.call(arguments, 0));
-                this.callbackContexts.push(undefined);
-                this.callArgProps.push(undefined);
-
-                return this;
-            },
-
-            yieldsOn: function (context) {
-                if (typeof context != "object") {
-                    throw new TypeError("argument context is not an object");
-                }
-
-                this.callArgAts.push(-1);
-                this.callbackArguments.push(slice.call(arguments, 1));
-                this.callbackContexts.push(context);
-                this.callArgProps.push(undefined);
-
-                return this;
-            },
-
-            yieldsTo: function (prop) {
-                this.callArgAts.push(-1);
-                this.callbackArguments.push(slice.call(arguments, 1));
-                this.callbackContexts.push(undefined);
-                this.callArgProps.push(prop);
-
-                return this;
-            },
-
-            yieldsToOn: function (prop, context) {
-                if (typeof context != "object") {
-                    throw new TypeError("argument context is not an object");
-                }
-
-                this.callArgAts.push(-1);
-                this.callbackArguments.push(slice.call(arguments, 2));
-                this.callbackContexts.push(context);
-                this.callArgProps.push(prop);
-
-                return this;
-            }
-        };
-
-        // create asynchronous versions of callsArg* and yields* methods
-        for (var method in proto) {
-            // need to avoid creating anotherasync versions of the newly added async methods
-            if (proto.hasOwnProperty(method) &&
-                method.match(/^(callsArg|yields|thenYields$)/) &&
-                !method.match(/Async/)) {
-                proto[method + 'Async'] = (function (syncFnName) {
-                    return function () {
-                        this.callbackAsync = true;
-                        return this[syncFnName].apply(this, arguments);
-                    };
-                })(method);
-            }
-        }
-
-        return proto;
-
-    }()));
-
-    if (commonJSModule) {
-        module.exports = stub;
-    } else {
-        sinon.stub = stub;
-    }
-}(typeof sinon == "object" && sinon || null));
-
-/**
- * @depend ../sinon.js
- * @depend stub.js
- */
-/*jslint eqeqeq: false, onevar: false, nomen: false*/
-/*global module, require, sinon*/
-/**
- * Mock functions.
- *
- * @author Christian Johansen ([email protected])
- * @license BSD
- *
- * Copyright (c) 2010-2011 Christian Johansen
- */
-
-(function (sinon) {
-    var commonJSModule = typeof module == "object" && typeof require == "function";
-    var push = [].push;
-
-    if (!sinon && commonJSModule) {
-        sinon = require("../sinon");
-    }
-
-    if (!sinon) {
-        return;
-    }
-
-    function mock(object) {
-        if (!object) {
-            return sinon.expectation.create("Anonymous mock");
-        }
-
-        return mock.create(object);
-    }
-
-    sinon.mock = mock;
-
-    sinon.extend(mock, (function () {
-        function each(collection, callback) {
-            if (!collection) {
-                return;
-            }
-
-            for (var i = 0, l = collection.length; i < l; i += 1) {
-                callback(collection[i]);
-            }
-        }
-
-        return {
-            create: function create(object) {
-                if (!object) {
-                    throw new TypeError("object is null");
-                }
-
-                var mockObject = sinon.extend({}, mock);
-                mockObject.object = object;
-                delete mockObject.create;
-
-                return mockObject;
-            },
-
-            expects: function expects(method) {
-                if (!method) {
-                    throw new TypeError("method is falsy");
-                }
-
-                if (!this.expectations) {
-                    this.expectations = {};
-                    this.proxies = [];
-                }
-
-                if (!this.expectations[method]) {
-                    this.expectations[method] = [];
-                    var mockObject = this;
-
-                    sinon.wrapMethod(this.object, method, function () {
-                        return mockObject.invokeMethod(method, this, arguments);
-                    });
-
-                    push.call(this.proxies, method);
-                }
-
-                var expectation = sinon.expectation.create(method);
-                push.call(this.expectations[method], expectation);
-
-                return expectation;
-            },
-
-            restore: function restore() {
-                var object = this.object;
-
-                each(this.proxies, function (proxy) {
-                    if (typeof object[proxy].restore == "function") {
-                        object[proxy].restore();
-                    }
-                });
-            },
-
-            verify: function verify() {
-                var expectations = this.expectations || {};
-                var messages = [], met = [];
-
-                each(this.proxies, function (proxy) {
-                    each(expectations[proxy], function (expectation) {
-                        if (!expectation.met()) {
-                            push.call(messages, expectation.toString());
-                        } else {
-                            push.call(met, expectation.toString());
-                        }
-                    });
-                });
-
-                this.restore();
-
-                if (messages.length > 0) {
-                    sinon.expectation.fail(messages.concat(met).join("\n"));
-                } else {
-                    sinon.expectation.pass(messages.concat(met).join("\n"));
-                }
-
-                return true;
-            },
-
-            invokeMethod: function invokeMethod(method, thisValue, args) {
-                var expectations = this.expectations && this.expectations[method];
-                var length = expectations && expectations.length || 0, i;
-
-                for (i = 0; i < length; i += 1) {
-                    if (!expectations[i].met() &&
-                        expectations[i].allowsCall(thisValue, args)) {
-                        return expectations[i].apply(thisValue, args);
-                    }
-                }
-
-                var messages = [], available, exhausted = 0;
-
-                for (i = 0; i < length; i += 1) {
-                    if (expectations[i].allowsCall(thisValue, args)) {
-                        available = available || expectations[i];
-                    } else {
-                        exhausted += 1;
-                    }
-                    push.call(messages, "    " + expectations[i].toString());
-                }
-
-                if (exhausted === 0) {
-                    return available.apply(thisValue, args);
-                }
-
-                messages.unshift("Unexpected call: " + sinon.spyCall.toString.call({
-                    proxy: method,
-                    args: args
-                }));
-
-                sinon.expectation.fail(messages.join("\n"));
-            }
-        };
-    }()));
-
-    var times = sinon.timesInWords;
-
-    sinon.expectation = (function () {
-        var slice = Array.prototype.slice;
-        var _invoke = sinon.spy.invoke;
-
-        function callCountInWords(callCount) {
-            if (callCount == 0) {
-                return "never called";
-            } else {
-                return "called " + times(callCount);
-            }
-        }
-
-        function expectedCallCountInWords(expectation) {
-            var min = expectation.minCalls;
-            var max = expectation.maxCalls;
-
-            if (typeof min == "number" && typeof max == "number") {
-                var str = times(min);
-
-                if (min != max) {
-                    str = "at least " + str + " and at most " + times(max);
-                }
-
-                return str;
-            }
-
-            if (typeof min == "number") {
-                return "at least " + times(min);
-            }
-
-            return "at most " + times(max);
-        }
-
-        function receivedMinCalls(expectation) {
-            var hasMinLimit = typeof expectation.minCalls == "number";
-            return !hasMinLimit || expectation.callCount >= expectation.minCalls;
-        }
-
-        function receivedMaxCalls(expectation) {
-            if (typeof expectation.maxCalls != "number") {
-                return false;
-            }
-
-            return expectation.callCount == expectation.maxCalls;
-        }
-
-        return {
-            minCalls: 1,
-            maxCalls: 1,
-
-            create: function create(methodName) {
-                var expectation = sinon.extend(sinon.stub.create(), sinon.expectation);
-                delete expectation.create;
-                expectation.method = methodName;
-
-                return expectation;
-            },
-
-            invoke: function invoke(func, thisValue, args) {
-                this.verifyCallAllowed(thisValue, args);
-
-                return _invoke.apply(this, arguments);
-            },
-
-            atLeast: function atLeast(num) {
-                if (typeof num != "number") {
-                    throw new TypeError("'" + num + "' is not number");
-                }
-
-                if (!this.limitsSet) {
-                    this.maxCalls = null;
-                    this.limitsSet = true;
-                }
-
-                this.minCalls = num;
-
-                return this;
-            },
-
-            atMost: function atMost(num) {
-                if (typeof num != "number") {
-                    throw new TypeError("'" + num + "' is not number");
-                }
-
-                if (!this.limitsSet) {
-                    this.minCalls = null;
-                    this.limitsSet = true;
-                }
-
-                this.maxCalls = num;
-
-                return this;
-            },
-
-            never: function never() {
-                return this.exactly(0);
-            },
-
-            once: function once() {
-                return this.exactly(1);
-            },
-
-            twice: function twice() {
-                return this.exactly(2);
-            },
-
-            thrice: function thrice() {
-                return this.exactly(3);
-            },
-
-            exactly: function exactly(num) {
-                if (typeof num != "number") {
-                    throw new TypeError("'" + num + "' is not a number");
-                }
-
-                this.atLeast(num);
-                return this.atMost(num);
-            },
-
-            met: function met() {
-                return !this.failed && receivedMinCalls(this);
-            },
-
-            verifyCallAllowed: function verifyCallAllowed(thisValue, args) {
-                if (receivedMaxCalls(this)) {
-                    this.failed = true;
-                    sinon.expectation.fail(this.method + " already called " + times(this.maxCalls));
-                }
-
-                if ("expectedThis" in this && this.expectedThis !== thisValue) {
-                    sinon.expectation.fail(this.method + " called with " + thisValue + " as thisValue, expected " +
-                        this.expectedThis);
-                }
-
-                if (!("expectedArguments" in this)) {
-                    return;
-                }
-
-                if (!args) {
-                    sinon.expectation.fail(this.method + " received no arguments, expected " +
-                        sinon.format(this.expectedArguments));
-                }
-
-                if (args.length < this.expectedArguments.length) {
-                    sinon.expectation.fail(this.method + " received too few arguments (" + sinon.format(args) +
-                        "), expected " + sinon.format(this.expectedArguments));
-                }
-
-                if (this.expectsExactArgCount &&
-                    args.length != this.expectedArguments.length) {
-                    sinon.expectation.fail(this.method + " received too many arguments (" + sinon.format(args) +
-                        "), expected " + sinon.format(this.expectedArguments));
-                }
-
-                for (var i = 0, l = this.expectedArguments.length; i < l; i += 1) {
-                    if (!sinon.deepEqual(this.expectedArguments[i], args[i])) {
-                        sinon.expectation.fail(this.method + " received wrong arguments " + sinon.format(args) +
-                            ", expected " + sinon.format(this.expectedArguments));
-                    }
-                }
-            },
-
-            allowsCall: function allowsCall(thisValue, args) {
-                if (this.met() && receivedMaxCalls(this)) {
-                    return false;
-                }
-
-                if ("expectedThis" in this && this.expectedThis !== thisValue) {
-                    return false;
-                }
-
-                if (!("expectedArguments" in this)) {
-                    return true;
-                }
-
-                args = args || [];
-
-                if (args.length < this.expectedArguments.length) {
-                    return false;
-                }
-
-                if (this.expectsExactArgCount &&
-                    args.length != this.expectedArguments.length) {
-                    return false;
-                }
-
-                for (var i = 0, l = this.expectedArguments.length; i < l; i += 1) {
-                    if (!sinon.deepEqual(this.expectedArguments[i], args[i])) {
-                        return false;
-                    }
-                }
-
-                return true;
-            },
-
-            withArgs: function withArgs() {
-                this.expectedArguments = slice.call(arguments);
-                return this;
-            },
-
-            withExactArgs: function withExactArgs() {
-                this.withArgs.apply(this, arguments);
-                this.expectsExactArgCount = true;
-                return this;
-            },
-
-            on: function on(thisValue) {
-                this.expectedThis = thisValue;
-                return this;
-            },
-
-            toString: function () {
-                var args = (this.expectedArguments || []).slice();
-
-                if (!this.expectsExactArgCount) {
-                    push.call(args, "[...]");
-                }
-
-                var callStr = sinon.spyCall.toString.call({
-                    proxy: this.method, args: args
-                });
-
-                var message = callStr.replace(", [...", "[, ...") + " " +
-                    expectedCallCountInWords(this);
-
-                if (this.met()) {
-                    return "Expectation met: " + message;
-                }
-
-                return "Expected " + message + " (" +
-                    callCountInWords(this.callCount) + ")";
-            },
-
-            verify: function verify() {
-                if (!this.met()) {
-                    sinon.expectation.fail(this.toString());
-                } else {
-                    sinon.expectation.pass(this.toString());
-                }
-
-                return true;
-            },
-
-            pass: function(message) {
-              sinon.assert.pass(message);
-            },
-            fail: function (message) {
-                var exception = new Error(message);
-                exception.name = "ExpectationError";
-
-                throw exception;
-            }
-        };
-    }());
-
-    if (commonJSModule) {
-        module.exports = mock;
-    } else {
-        sinon.mock = mock;
-    }
-}(typeof sinon == "object" && sinon || null));
-
-/**
- * @depend ../sinon.js
- * @depend stub.js
- * @depend mock.js
- */
-/*jslint eqeqeq: false, onevar: false, forin: true*/
-/*global module, require, sinon*/
-/**
- * Collections of stubs, spies and mocks.
- *
- * @author Christian Johansen ([email protected])
- * @license BSD
- *
- * Copyright (c) 2010-2011 Christian Johansen
- */
-
-(function (sinon) {
-    var commonJSModule = typeof module == "object" && typeof require == "function";
-    var push = [].push;
-    var hasOwnProperty = Object.prototype.hasOwnProperty;
-
-    if (!sinon && commonJSModule) {
-        sinon = require("../sinon");
-    }
-
-    if (!sinon) {
-        return;
-    }
-
-    function getFakes(fakeCollection) {
-        if (!fakeCollection.fakes) {
-            fakeCollection.fakes = [];
-        }
-
-        return fakeCollection.fakes;
-    }
-
-    function each(fakeCollection, method) {
-        var fakes = getFakes(fakeCollection);
-
-        for (var i = 0, l = fakes.length; i < l; i += 1) {
-            if (typeof fakes[i][method] == "function") {
-                fakes[i][method]();
-            }
-        }
-    }
-
-    function compact(fakeCollection) {
-        var fakes = getFakes(fakeCollection);
-        var i = 0;
-        while (i < fakes.length) {
-          fakes.splice(i, 1);
-        }
-    }
-
-    var collection = {
-        verify: function resolve() {
-            each(this, "verify");
-        },
-
-        restore: function restore() {
-            each(this, "restore");
-            compact(this);
-        },
-
-        verifyAndRestore: function verifyAndRestore() {
-            var exception;
-
-            try {
-                this.verify();
-            } catch (e) {
-                exception = e;
-            }
-
-            this.restore();
-
-            if (exception) {
-                throw exception;
-            }
-        },
-
-        add: function add(fake) {
-            push.call(getFakes(this), fake);
-            return fake;
-        },
-
-        spy: function spy() {
-            return this.add(sinon.spy.apply(sinon, arguments));
-        },
-
-        stub: function stub(object, property, value) {
-            if (property) {
-                var original = object[property];
-
-                if (typeof original != "function") {
-                    if (!hasOwnProperty.call(object, property)) {
-                        throw new TypeError("Cannot stub non-existent own property " + property);
-                    }
-
-                    object[property] = value;
-
-                    return this.add({
-                        restore: function () {
-                            object[property] = original;
-                        }
-                    });
-                }
-            }
-            if (!property && !!object && typeof object == "object") {
-                var stubbedObj = sinon.stub.apply(sinon, arguments);
-
-                for (var prop in stubbedObj) {
-                    if (typeof stubbedObj[prop] === "function") {
-                        this.add(stubbedObj[prop]);
-                    }
-                }
-
-                return stubbedObj;
-            }
-
-            return this.add(sinon.stub.apply(sinon, arguments));
-        },
-
-        mock: function mock() {
-            return this.add(sinon.mock.apply(sinon, arguments));
-        },
-
-        inject: function inject(obj) {
-            var col = this;
-
-            obj.spy = function () {
-                return col.spy.apply(col, arguments);
-            };
-
-            obj.stub = function () {
-                return col.stub.apply(col, arguments);
-            };
-
-            obj.mock = function () {
-                return col.mock.apply(col, arguments);
-            };
-
-            return obj;
-        }
-    };
-
-    if (commonJSModule) {
-        module.exports = collection;
-    } else {
-        sinon.collection = collection;
-    }
-}(typeof sinon == "object" && sinon || null));
-
-/*jslint eqeqeq: false, plusplus: false, evil: true, onevar: false, browser: true, forin: false*/
-/*global module, require, window*/
-/**
- * Fake timer API
- * setTimeout
- * setInterval
- * clearTimeout
- * clearInterval
- * tick
- * reset
- * Date
- *
- * Inspired by jsUnitMockTimeOut from JsUnit
- *
- * @author Christian Johansen ([email protected])
- * @license BSD
- *
- * Copyright (c) 2010-2011 Christian Johansen
- */
-
-if (typeof sinon == "undefined") {
-    var sinon = {};
-}
-
-(function (global) {
-    var id = 1;
-
-    function addTimer(args, recurring) {
-        if (args.length === 0) {
-            throw new Error("Function requires at least 1 parameter");
-        }
-
-        var toId = id++;
-        var delay = args[1] || 0;
-
-        if (!this.timeouts) {
-            this.timeouts = {};
-        }
-
-        this.timeouts[toId] = {
-            id: toId,
-            func: args[0],
-            callAt: this.now + delay,
-            invokeArgs: Array.prototype.slice.call(args, 2)
-        };
-
-        if (recurring === true) {
-            this.timeouts[toId].interval = delay;
-        }
-
-        return toId;
-    }
-
-    function parseTime(str) {
-        if (!str) {
-            return 0;
-        }
-
-        var strings = str.split(":");
-        var l = strings.length, i = l;
-        var ms = 0, parsed;
-
-        if (l > 3 || !/^(\d\d:){0,2}\d\d?$/.test(str)) {
-            throw new Error("tick only understands numbers and 'h:m:s'");
-        }
-
-        while (i--) {
-            parsed = parseInt(strings[i], 10);
-
-            if (parsed >= 60) {
-                throw new Error("Invalid time " + str);
-            }
-
-            ms += parsed * Math.pow(60, (l - i - 1));
-        }
-
-        return ms * 1000;
-    }
-
-    function createObject(object) {
-        var newObject;
-
-        if (Object.create) {
-            newObject = Object.create(object);
-        } else {
-            var F = function () {};
-            F.prototype = object;
-            newObject = new F();
-        }
-
-        newObject.Date.clock = newObject;
-        return newObject;
-    }
-
-    sinon.clock = {
-        now: 0,
-
-        create: function create(now) {
-            var clock = createObject(this);
-
-            if (typeof now == "number") {
-                clock.now = now;
-            }
-
-            if (!!now && typeof now == "object") {
-                throw new TypeError("now should be milliseconds since UNIX epoch");
-            }
-
-            return clock;
-        },
-
-        setTimeout: function setTimeout(callback, timeout) {
-            return addTimer.call(this, arguments, false);
-        },
-
-        clearTimeout: function clearTimeout(timerId) {
-            if (!this.timeouts) {
-                this.timeouts = [];
-            }
-
-            if (timerId in this.timeouts) {
-                delete this.timeouts[timerId];
-            }
-        },
-
-        setInterval: function setInterval(callback, timeout) {
-            return addTimer.call(this, arguments, true);
-        },
-
-        clearInterval: function clearInterval(timerId) {
-            this.clearTimeout(timerId);
-        },
-
-        tick: function tick(ms) {
-            ms = typeof ms == "number" ? ms : parseTime(ms);
-            var tickFrom = this.now, tickTo = this.now + ms, previous = this.now;
-            var timer = this.firstTimerInRange(tickFrom, tickTo);
-
-            var firstException;
-            while (timer && tickFrom <= tickTo) {
-                if (this.timeouts[timer.id]) {
-                    tickFrom = this.now = timer.callAt;
-                    try {
-                      this.callTimer(timer);
-                    } catch (e) {
-                      firstException = firstException || e;
-                    }
-                }
-
-                timer = this.firstTimerInRange(previous, tickTo);
-                previous = tickFrom;
-            }
-
-            this.now = tickTo;
-
-            if (firstException) {
-              throw firstException;
-            }
-        },
-
-        firstTimerInRange: function (from, to) {
-            var timer, smallest, originalTimer;
-
-            for (var id in this.timeouts) {
-                if (this.timeouts.hasOwnProperty(id)) {
-                    if (this.timeouts[id].callAt < from || this.timeouts[id].callAt > to) {
-                        continue;
-                    }
-
-                    if (!smallest || this.timeouts[id].callAt < smallest) {
-                        originalTimer = this.timeouts[id];
-                        smallest = this.timeouts[id].callAt;
-
-                        timer = {
-                            func: this.timeouts[id].func,
-                            callAt: this.timeouts[id].callAt,
-                            interval: this.timeouts[id].interval,
-                            id: this.timeouts[id].id,
-                            invokeArgs: this.timeouts[id].invokeArgs
-                        };
-                    }
-                }
-            }
-
-            return timer || null;
-        },
-
-        callTimer: function (timer) {
-            if (typeof timer.interval == "number") {
-                this.timeouts[timer.id].callAt += timer.interval;
-            } else {
-                delete this.timeouts[timer.id];
-            }
-
-            try {
-                if (typeof timer.func == "function") {
-                    timer.func.apply(null, timer.invokeArgs);
-                } else {
-                    eval(timer.func);
-                }
-            } catch (e) {
-              var exception = e;
-            }
-
-            if (!this.timeouts[timer.id]) {
-                if (exception) {
-                  throw exception;
-                }
-                return;
-            }
-
-            if (exception) {
-              throw exception;
-            }
-        },
-
-        reset: function reset() {
-            this.timeouts = {};
-        },
-
-        Date: (function () {
-            var NativeDate = Date;
-
-            function ClockDate(year, month, date, hour, minute, second, ms) {
-                // Defensive and verbose to avoid potential harm in passing
-                // explicit undefined when user does not pass argument
-                switch (arguments.length) {
-                case 0:
-                    return new NativeDate(ClockDate.clock.now);
-                case 1:
-                    return new NativeDate(year);
-                case 2:
-                    return new NativeDate(year, month);
-                case 3:
-                    return new NativeDate(year, month, date);
-                case 4:
-                    return new NativeDate(year, month, date, hour);
-                case 5:
-                    return new NativeDate(year, month, date, hour, minute);
-                case 6:
-                    return new NativeDate(year, month, date, hour, minute, second);
-                default:
-                    return new NativeDate(year, month, date, hour, minute, second, ms);
-                }
-            }
-
-            return mirrorDateProperties(ClockDate, NativeDate);
-        }())
-    };
-
-    function mirrorDateProperties(target, source) {
-        if (source.now) {
-            target.now = function now() {
-                return target.clock.now;
-            };
-        } else {
-            delete target.now;
-        }
-
-        if (source.toSource) {
-            target.toSource = function toSource() {
-                return source.toSource();
-            };
-        } else {
-            delete target.toSource;
-        }
-
-        target.toString = function toString() {
-            return source.toString();
-        };
-
-        target.prototype = source.prototype;
-        target.parse = source.parse;
-        target.UTC = source.UTC;
-        target.prototype.toUTCString = source.prototype.toUTCString;
-        return target;
-    }
-
-    var methods = ["Date", "setTimeout", "setInterval",
-                   "clearTimeout", "clearInterval"];
-
-    function restore() {
-        var method;
-
-        for (var i = 0, l = this.methods.length; i < l; i++) {
-            method = this.methods[i];
-            if (global[method].hadOwnProperty) {
-                global[method] = this["_" + method];
-            } else {
-                delete global[method];
-            }
-        }
-
-        // Prevent multiple executions which will completely remove these props
-        this.methods = [];
-    }
-
-    function stubGlobal(method, clock) {
-        clock[method].hadOwnProperty = Object.prototype.hasOwnProperty.call(global, method);
-        clock["_" + method] = global[method];
-
-        if (method == "Date") {
-            var date = mirrorDateProperties(clock[method], global[method]);
-            global[method] = date;
-        } else {
-            global[method] = function () {
-                return clock[method].apply(clock, arguments);
-            };
-
-            for (var prop in clock[method]) {
-                if (clock[method].hasOwnProperty(prop)) {
-                    global[method][prop] = clock[method][prop];
-                }
-            }
-        }
-
-        global[method].clock = clock;
-    }
-
-    sinon.useFakeTimers = function useFakeTimers(now) {
-        var clock = sinon.clock.create(now);
-        clock.restore = restore;
-        clock.methods = Array.prototype.slice.call(arguments,
-                                                   typeof now == "number" ? 1 : 0);
-
-        if (clock.methods.length === 0) {
-            clock.methods = methods;
-        }
-
-        for (var i = 0, l = clock.methods.length; i < l; i++) {
-            stubGlobal(clock.methods[i], clock);
-        }
-
-        return clock;
-    };
-}(typeof global != "undefined" && typeof global !== "function" ? global : this));
-
-sinon.timers = {
-    setTimeout: setTimeout,
-    clearTimeout: clearTimeout,
-    setInterval: setInterval,
-    clearInterval: clearInterval,
-    Date: Date
-};
-
-if (typeof module == "object" && typeof require == "function") {
-    module.exports = sinon;
-}
-
-/*jslint eqeqeq: false, onevar: false*/
-/*global sinon, module, require, ActiveXObject, XMLHttpRequest, DOMParser*/
-/**
- * Minimal Event interface implementation
- *
- * Original implementation by Sven Fuchs: https://gist.github.com/995028
- * Modifications and tests by Christian Johansen.
- *
- * @author Sven Fuchs ([email protected])
- * @author Christian Johansen ([email protected])
- * @license BSD
- *
- * Copyright (c) 2011 Sven Fuchs, Christian Johansen
- */
-
-if (typeof sinon == "undefined") {
-    this.sinon = {};
-}
-
-(function () {
-    var push = [].push;
-
-    sinon.Event = function Event(type, bubbles, cancelable) {
-        this.initEvent(type, bubbles, cancelable);
-    };
-
-    sinon.Event.prototype = {
-        initEvent: function(type, bubbles, cancelable) {
-            this.type = type;
-            this.bubbles = bubbles;
-            this.cancelable = cancelable;
-        },
-
-        stopPropagation: function () {},
-
-        preventDefault: function () {
-            this.defaultPrevented = true;
-        }
-    };
-
-    sinon.EventTarget = {
-        addEventListener: function addEventListener(event, listener, useCapture) {
-            this.eventListeners = this.eventListeners || {};
-            this.eventListeners[event] = this.eventListeners[event] || [];
-            push.call(this.eventListeners[event], listener);
-        },
-
-        removeEventListener: function removeEventListener(event, listener, useCapture) {
-            var listeners = this.eventListeners && this.eventListeners[event] || [];
-
-            for (var i = 0, l = listeners.length; i < l; ++i) {
-                if (listeners[i] == listener) {
-                    return listeners.splice(i, 1);
-                }
-            }
-        },
-
-        dispatchEvent: function dispatchEvent(event) {
-            var type = event.type;
-            var listeners = this.eventListeners && this.eventListeners[type] || [];
-
-            for (var i = 0; i < listeners.length; i++) {
-                if (typeof listeners[i] == "function") {
-                    listeners[i].call(this, event);
-                } else {
-                    listeners[i].handleEvent(event);
-                }
-            }
-
-            return !!event.defaultPrevented;
-        }
-    };
-}());
-
-/**
- * @depend ../../sinon.js
- * @depend event.js
- */
-/*jslint eqeqeq: false, onevar: false*/
-/*global sinon, module, require, ActiveXObject, XMLHttpRequest, DOMParser*/
-/**
- * Fake XMLHttpRequest object
- *
- * @author Christian Johansen ([email protected])
- * @license BSD
- *
- * Copyright (c) 2010-2011 Christian Johansen
- */
-
-if (typeof sinon == "undefined") {
-    this.sinon = {};
-}
-sinon.xhr = { XMLHttpRequest: this.XMLHttpRequest };
-
-// wrapper for global
-(function(global) {
-    var xhr = sinon.xhr;
-    xhr.GlobalXMLHttpRequest = global.XMLHttpRequest;
-    xhr.GlobalActiveXObject = global.ActiveXObject;
-    xhr.supportsActiveX = typeof xhr.GlobalActiveXObject != "undefined";
-    xhr.supportsXHR = typeof xhr.GlobalXMLHttpRequest != "undefined";
-    xhr.workingXHR = xhr.supportsXHR ? xhr.GlobalXMLHttpRequest : xhr.supportsActiveX
-                                     ? function() { return new xhr.GlobalActiveXObject("MSXML2.XMLHTTP.3.0") } : false;
-
-    /*jsl:ignore*/
-    var unsafeHeaders = {
-        "Accept-Charset": true,
-        "Accept-Encoding": true,
-        "Connection": true,
-        "Content-Length": true,
-        "Cookie": true,
-        "Cookie2": true,
-        "Content-Transfer-Encoding": true,
-        "Date": true,
-        "Expect": true,
-        "Host": true,
-        "Keep-Alive": true,
-        "Referer": true,
-        "TE": true,
-        "Trailer": true,
-        "Transfer-Encoding": true,
-        "Upgrade": true,
-        "User-Agent": true,
-        "Via": true
-    };
-    /*jsl:end*/
-
-    function FakeXMLHttpRequest() {
-        this.readyState = FakeXMLHttpRequest.UNSENT;
-        this.requestHeaders = {};
-        this.requestBody = null;
-        this.status = 0;
-        this.statusText = "";
-
-        if (typeof FakeXMLHttpRequest.onCreate == "function") {
-            FakeXMLHttpRequest.onCreate(this);
-        }
-    }
-
-    function verifyState(xhr) {
-        if (xhr.readyState !== FakeXMLHttpRequest.OPENED) {
-            throw new Error("INVALID_STATE_ERR");
-        }
-
-        if (xhr.sendFlag) {
-            throw new Error("INVALID_STATE_ERR");
-        }
-    }
-
-    // filtering to enable a white-list version of Sinon FakeXhr,
-    // where whitelisted requests are passed through to real XHR
-    function each(collection, callback) {
-        if (!collection) return;
-        for (var i = 0, l = collection.length; i < l; i += 1) {
-            callback(collection[i]);
-        }
-    }
-    function some(collection, callback) {
-        for (var index = 0; index < collection.length; index++) {
-            if(callback(collection[index]) === true) return true;
-        };
-        return false;
-    }
-    // largest arity in XHR is 5 - XHR#open
-    var apply = function(obj,method,args) {
-        switch(args.length) {
-        case 0: return obj[method]();
-        case 1: return obj[method](args[0]);
-        case 2: return obj[method](args[0],args[1]);
-        case 3: return obj[method](args[0],args[1],args[2]);
-        case 4: return obj[method](args[0],args[1],args[2],args[3]);
-        case 5: return obj[method](args[0],args[1],args[2],args[3],args[4]);
-        };
-    };
-
-    FakeXMLHttpRequest.filters = [];
-    FakeXMLHttpRequest.addFilter = function(fn) {
-        this.filters.push(fn)
-    };
-    var IE6Re = /MSIE 6/;
-    FakeXMLHttpRequest.defake = function(fakeXhr,xhrArgs) {
-        var xhr = new sinon.xhr.workingXHR();
-        each(["open","setRequestHeader","send","abort","getResponseHeader",
-              "getAllResponseHeaders","addEventListener","overrideMimeType","removeEventListener"],
-             function(method) {
-                 fakeXhr[method] = function() {
-                   return apply(xhr,method,arguments);
-                 };
-             });
-
-        var copyAttrs = function(args) {
-            each(args, function(attr) {
-              try {
-                fakeXhr[attr] = xhr[attr]
-              } catch(e) {
-                if(!IE6Re.test(navigator.userAgent)) throw e;
-              }
-            });
-        };
-
-        var stateChange = function() {
-            fakeXhr.readyState = xhr.readyState;
-            if(xhr.readyState >= FakeXMLHttpRequest.HEADERS_RECEIVED) {
-                copyAttrs(["status","statusText"]);
-            }
-            if(xhr.readyState >= FakeXMLHttpRequest.LOADING) {
-                copyAttrs(["responseText"]);
-            }
-            if(xhr.readyState === FakeXMLHttpRequest.DONE) {
-                copyAttrs(["responseXML"]);
-            }
-            if(fakeXhr.onreadystatechange) fakeXhr.onreadystatechange.call(fakeXhr);
-        };
-        if(xhr.addEventListener) {
-          for(var event in fakeXhr.eventListeners) {
-              if(fakeXhr.eventListeners.hasOwnProperty(event)) {
-                  each(fakeXhr.eventListeners[event],function(handler) {
-                      xhr.addEventListener(event, handler);
-                  });
-              }
-          }
-          xhr.addEventListener("readystatechange",stateChange);
-        } else {
-          xhr.onreadystatechange = stateChange;
-        }
-        apply(xhr,"open",xhrArgs);
-    };
-    FakeXMLHttpRequest.useFilters = false;
-
-    function verifyRequestSent(xhr) {
-        if (xhr.readyState == FakeXMLHttpRequest.DONE) {
-            throw new Error("Request done");
-        }
-    }
-
-    function verifyHeadersReceived(xhr) {
-        if (xhr.async && xhr.readyState != FakeXMLHttpRequest.HEADERS_RECEIVED) {
-            throw new Error("No headers received");
-        }
-    }
-
-    function verifyResponseBodyType(body) {
-        if (typeof body != "string") {
-            var error = new Error("Attempted to respond to fake XMLHttpRequest with " +
-                                 body + ", which is not a string.");
-            error.name = "InvalidBodyException";
-            throw error;
-        }
-    }
-
-    sinon.extend(FakeXMLHttpRequest.prototype, sinon.EventTarget, {
-        async: true,
-
-        open: function open(method, url, async, username, password) {
-            this.method = method;
-            this.url = url;
-            this.async = typeof async == "boolean" ? async : true;
-            this.username = username;
-            this.password = password;
-            this.responseText = null;
-            this.responseXML = null;
-            this.requestHeaders = {};
-            this.sendFlag = false;
-            if(sinon.FakeXMLHttpRequest.useFilters === true) {
-                var xhrArgs = arguments;
-                var defake = some(FakeXMLHttpRequest.filters,function(filter) {
-                    return filter.apply(this,xhrArgs)
-                });
-                if (defake) {
-                  return sinon.FakeXMLHttpRequest.defake(this,arguments);
-                }
-            }
-            this.readyStateChange(FakeXMLHttpRequest.OPENED);
-        },
-
-        readyStateChange: function readyStateChange(state) {
-            this.readyState = state;
-
-            if (typeof this.onreadystatechange == "function") {
-                try {
-                    this.onreadystatechange();
-                } catch (e) {
-                    sinon.logError("Fake XHR onreadystatechange handler", e);
-                }
-            }
-
-            this.dispatchEvent(new sinon.Event("readystatechange"));
-        },
-
-        setRequestHeader: function setRequestHeader(header, value) {
-            verifyState(this);
-
-            if (unsafeHeaders[header] || /^(Sec-|Proxy-)/.test(header)) {
-                throw new Error("Refused to set unsafe header \"" + header + "\"");
-            }
-
-            if (this.requestHeaders[header]) {
-                this.requestHeaders[header] += "," + value;
-            } else {
-                this.requestHeaders[header] = value;
-            }
-        },
-
-        // Helps testing
-        setResponseHeaders: function setResponseHeaders(headers) {
-            this.responseHeaders = {};
-
-            for (var header in headers) {
-                if (headers.hasOwnProperty(header)) {
-                    this.responseHeaders[header] = headers[header];
-                }
-            }
-
-            if (this.async) {
-                this.readyStateChange(FakeXMLHttpRequest.HEADERS_RECEIVED);
-            }
-        },
-
-        // Currently treats ALL data as a DOMString (i.e. no Document)
-        send: function send(data) {
-            verifyState(this);
-
-            if (!/^(get|head)$/i.test(this.method)) {
-                if (this.requestHeaders["Content-Type"]) {
-                    var value = this.requestHeaders["Content-Type"].split(";");
-                    this.requestHeaders["Content-Type"] = value[0] + ";charset=utf-8";
-                } else {
-                    this.requestHeaders["Content-Type"] = "text/plain;charset=utf-8";
-                }
-
-                this.requestBody = data;
-            }
-
-            this.errorFlag = false;
-            this.sendFlag = this.async;
-            this.readyStateChange(FakeXMLHttpRequest.OPENED);
-
-            if (typeof this.onSend == "function") {
-                this.onSend(this);
-            }
-        },
-
-        abort: function abort() {
-            this.aborted = true;
-            this.responseText = null;
-            this.errorFlag = true;
-            this.requestHeaders = {};
-
-            if (this.readyState > sinon.FakeXMLHttpRequest.UNSENT && this.sendFlag) {
-                this.readyStateChange(sinon.FakeXMLHttpRequest.DONE);
-                this.sendFlag = false;
-            }
-
-            this.readyState = sinon.FakeXMLHttpRequest.UNSENT;
-        },
-
-        getResponseHeader: function getResponseHeader(header) {
-            if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) {
-                return null;
-            }
-
-            if (/^Set-Cookie2?$/i.test(header)) {
-                return null;
-            }
-
-            header = header.toLowerCase();
-
-            for (var h in this.responseHeaders) {
-                if (h.toLowerCase() == header) {
-                    return this.responseHeaders[h];
-                }
-            }
-
-            return null;
-        },
-
-        getAllResponseHeaders: function getAllResponseHeaders() {
-            if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) {
-                return "";
-            }
-
-            var headers = "";
-
-            for (var header in this.responseHeaders) {
-                if (this.responseHeaders.hasOwnProperty(header) &&
-                    !/^Set-Cookie2?$/i.test(header)) {
-                    headers += header + ": " + this.responseHeaders[header] + "\r\n";
-                }
-            }
-
-            return headers;
-        },
-
-        setResponseBody: function setResponseBody(body) {
-            verifyRequestSent(this);
-            verifyHeadersReceived(this);
-            verifyResponseBodyType(body);
-
-            var chunkSize = this.chunkSize || 10;
-            var index = 0;
-            this.responseText = "";
-
-            do {
-                if (this.async) {
-                    this.readyStateChange(FakeXMLHttpRequest.LOADING);
-                }
-
-                this.responseText += body.substring(index, index + chunkSize);
-                index += chunkSize;
-            } while (index < body.length);
-
-            var type = this.getResponseHeader("Content-Type");
-
-            if (this.responseText &&
-                (!type || /(text\/xml)|(application\/xml)|(\+xml)/.test(type))) {
-                try {
-                    this.responseXML = FakeXMLHttpRequest.parseXML(this.responseText);
-                } catch (e) {
-                    // Unable to parse XML - no biggie
-                }
-            }
-
-            if (this.async) {
-                this.readyStateChange(FakeXMLHttpRequest.DONE);
-            } else {
-                this.readyState = FakeXMLHttpRequest.DONE;
-            }
-        },
-
-        respond: function respond(status, headers, body) {
-            this.setResponseHeaders(headers || {});
-            this.status = typeof status == "number" ? status : 200;
-            this.statusText = FakeXMLHttpRequest.statusCodes[this.status];
-            this.setResponseBody(body || "");
-        }
-    });
-
-    sinon.extend(FakeXMLHttpRequest, {
-        UNSENT: 0,
-        OPENED: 1,
-        HEADERS_RECEIVED: 2,
-        LOADING: 3,
-        DONE: 4
-    });
-
-    // Borrowed from JSpec
-    FakeXMLHttpRequest.parseXML = function parseXML(text) {
-        var xmlDoc;
-
-        if (typeof DOMParser != "undefined") {
-            var parser = new DOMParser();
-            xmlDoc = parser.parseFromString(text, "text/xml");
-        } else {
-            xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
-            xmlDoc.async = "false";
-            xmlDoc.loadXML(text);
-        }
-
-        return xmlDoc;
-    };
-
-    FakeXMLHttpRequest.statusCodes = {
-        100: "Continue",
-        101: "Switching Protocols",
-        200: "OK",
-        201: "Created",
-        202: "Accepted",
-        203: "Non-Authoritative Information",
-        204: "No Content",
-        205: "Reset Content",
-        206: "Partial Content",
-        300: "Multiple Choice",
-        301: "Moved Permanently",
-        302: "Found",
-        303: "See Other",
-        304: "Not Modified",
-        305: "Use Proxy",
-        307: "Temporary Redirect",
-        400: "Bad Request",
-        401: "Unauthorized",
-        402: "Payment Required",
-        403: "Forbidden",
-        404: "Not Found",
-        405: "Method Not Allowed",
-        406: "Not Acceptable",
-        407: "Proxy Authentication Required",
-        408: "Request Timeout",
-        409: "Conflict",
-        410: "Gone",
-        411: "Length Required",
-        412: "Precondition Failed",
-        413: "Request Entity Too Large",
-        414: "Request-URI Too Long",
-        415: "Unsupported Media Type",
-        416: "Requested Range Not Satisfiable",
-        417: "Expectation Failed",
-        422: "Unprocessable Entity",
-        500: "Internal Server Error",
-        501: "Not Implemented",
-        502: "Bad Gateway",
-        503: "Service Unavailable",
-        504: "Gateway Timeout",
-        505: "HTTP Version Not Supported"
-    };
-
-    sinon.useFakeXMLHttpRequest = function () {
-        sinon.FakeXMLHttpRequest.restore = function restore(keepOnCreate) {
-            if (xhr.supportsXHR) {
-                global.XMLHttpRequest = xhr.GlobalXMLHttpRequest;
-            }
-
-            if (xhr.supportsActiveX) {
-                global.ActiveXObject = xhr.GlobalActiveXObject;
-            }
-
-            delete sinon.FakeXMLHttpRequest.restore;
-
-            if (keepOnCreate !== true) {
-                delete sinon.FakeXMLHttpRequest.onCreate;
-            }
-        };
-        if (xhr.supportsXHR) {
-            global.XMLHttpRequest = sinon.FakeXMLHttpRequest;
-        }
-
-        if (xhr.supportsActiveX) {
-            global.ActiveXObject = function ActiveXObject(objId) {
-                if (objId == "Microsoft.XMLHTTP" || /^Msxml2\.XMLHTTP/i.test(objId)) {
-
-                    return new sinon.FakeXMLHttpRequest();
-                }
-
-                return new xhr.GlobalActiveXObject(objId);
-            };
-        }
-
-        return sinon.FakeXMLHttpRequest;
-    };
-
-    sinon.FakeXMLHttpRequest = FakeXMLHttpRequest;
-})(this);
-
-if (typeof module == "object" && typeof require == "function") {
-    module.exports = sinon;
-}
-
-/**
- * @depend fake_xml_http_request.js
- */
-/*jslint eqeqeq: false, onevar: false, regexp: false, plusplus: false*/
-/*global module, require, window*/
-/**
- * The Sinon "server" mimics a web server that receives requests from
- * sinon.FakeXMLHttpRequest and provides an API to respond to those requests,
- * both synchronously and asynchronously. To respond synchronuously, canned
- * answers have to be provided upfront.
- *
- * @author Christian Johansen ([email protected])
- * @license BSD
- *
- * Copyright (c) 2010-2011 Christian Johansen
- */
-
-if (typeof sinon == "undefined") {
-    var sinon = {};
-}
-
-sinon.fakeServer = (function () {
-    var push = [].push;
-    function F() {}
-
-    function create(proto) {
-        F.prototype = proto;
-        return new F();
-    }
-
-    function responseArray(handler) {
-        var response = handler;
-
-        if (Object.prototype.toString.call(handler) != "[object Array]") {
-            response = [200, {}, handler];
-        }
-
-        if (typeof response[2] != "string") {
-            throw new TypeError("Fake server response body should be string, but was " +
-                                typeof response[2]);
-        }
-
-        return response;
-    }
-
-    var wloc = typeof window !== "undefined" ? window.location : {};
-    var rCurrLoc = new RegExp("^" + wloc.protocol + "//" + wloc.host);
-
-    function matchOne(response, reqMethod, reqUrl) {
-        var rmeth = response.method;
-        var matchMethod = !rmeth || rmeth.toLowerCase() == reqMethod.toLowerCase();
-        var url = response.url;
-        var matchUrl = !url || url == reqUrl || (typeof url.test == "function" && url.test(reqUrl));
-
-        return matchMethod && matchUrl;
-    }
-
-    function match(response, request) {
-        var requestMethod = this.getHTTPMethod(request);
-        var requestUrl = request.url;
-
-        if (!/^https?:\/\//.test(requestUrl) || rCurrLoc.test(requestUrl)) {
-            requestUrl = requestUrl.replace(rCurrLoc, "");
-        }
-
-        if (matchOne(response, this.getHTTPMethod(request), requestUrl)) {
-            if (typeof response.response == "function") {
-                var ru = response.url;
-                var args = [request].concat(!ru ? [] : requestUrl.match(ru).slice(1));
-                return response.response.apply(response, args);
-            }
-
-            return true;
-        }
-
-        return false;
-    }
-
-    return {
-        create: function () {
-            var server = create(this);
-            this.xhr = sinon.useFakeXMLHttpRequest();
-            server.requests = [];
-
-            this.xhr.onCreate = function (xhrObj) {
-                server.addRequest(xhrObj);
-            };
-
-            return server;
-        },
-
-        addRequest: function addRequest(xhrObj) {
-            var server = this;
-            push.call(this.requests, xhrObj);
-
-            xhrObj.onSend = function () {
-                server.handleRequest(this);
-            };
-
-            if (this.autoRespond && !this.responding) {
-                setTimeout(function () {
-                    server.responding = false;
-                    server.respond();
-                }, this.autoRespondAfter || 10);
-
-                this.responding = true;
-            }
-        },
-
-        getHTTPMethod: function getHTTPMethod(request) {
-            if (this.fakeHTTPMethods && /post/i.test(request.method)) {
-                var matches = (request.requestBody || "").match(/_method=([^\b;]+)/);
-                return !!matches ? matches[1] : request.method;
-            }
-
-            return request.method;
-        },
-
-        handleRequest: function handleRequest(xhr) {
-            if (xhr.async) {
-                if (!this.queue) {
-                    this.queue = [];
-                }
-
-                push.call(this.queue, xhr);
-            } else {
-                this.processRequest(xhr);
-            }
-        },
-
-        respondWith: function respondWith(method, url, body) {
-            if (arguments.length == 1 && typeof method != "function") {
-                this.response = responseArray(method);
-                return;
-            }
-
-            if (!this.responses) { this.responses = []; }
-
-            if (arguments.length == 1) {
-                body = method;
-                url = method = null;
-            }
-
-            if (arguments.length == 2) {
-                body = url;
-                url = method;
-                method = null;
-            }
-
-            push.call(this.responses, {
-                method: method,
-                url: url,
-                response: typeof body == "function" ? body : responseArray(body)
-            });
-        },
-
-        respond: function respond() {
-            if (arguments.length > 0) this.respondWith.apply(this, arguments);
-            var queue = this.queue || [];
-            var request;
-
-            while(request = queue.shift()) {
-                this.processRequest(request);
-            }
-        },
-
-        processRequest: function processRequest(request) {
-            try {
-                if (request.aborted) {
-                    return;
-                }
-
-                var response = this.response || [404, {}, ""];
-
-                if (this.responses) {
-                    for (var i = 0, l = this.responses.length; i < l; i++) {
-                        if (match.call(this, this.responses[i], request)) {
-                            response = this.responses[i].response;
-                            break;
-                        }
-                    }
-                }
-
-                if (request.readyState != 4) {
-                    request.respond(response[0], response[1], response[2]);
-                }
-            } catch (e) {
-                sinon.logError("Fake server request processing", e);
-            }
-        },
-
-        restore: function restore() {
-            return this.xhr.restore && this.xhr.restore.apply(this.xhr, arguments);
-        }
-    };
-}());
-
-if (typeof module == "object" && typeof require == "function") {
-    module.exports = sinon;
-}
-
-/**
- * @depend fake_server.js
- * @depend fake_timers.js
- */
-/*jslint browser: true, eqeqeq: false, onevar: false*/
-/*global sinon*/
-/**
- * Add-on for sinon.fakeServer that automatically handles a fake timer along with
- * the FakeXMLHttpRequest. The direct inspiration for this add-on is jQuery
- * 1.3.x, which does not use xhr object's onreadystatehandler at all - instead,
- * it polls the object for completion with setInterval. Dispite the direct
- * motivation, there is nothing jQuery-specific in this file, so it can be used
- * in any environment where the ajax implementation depends on setInterval or
- * setTimeout.
- *
- * @author Christian Johansen ([email protected])
- * @license BSD
- *
- * Copyright (c) 2010-2011 Christian Johansen
- */
-
-(function () {
-    function Server() {}
-    Server.prototype = sinon.fakeServer;
-
-    sinon.fakeServerWithClock = new Server();
-
-    sinon.fakeServerWithClock.addRequest = function addRequest(xhr) {
-        if (xhr.async) {
-            if (typeof setTimeout.clock == "object") {
-                this.clock = setTimeout.clock;
-            } else {
-                this.clock = sinon.useFakeTimers();
-                this.resetClock = true;
-            }
-
-            if (!this.longestTimeout) {
-                var clockSetTimeout = this.clock.setTimeout;
-                var clockSetInterval = this.clock.setInterval;
-                var server = this;
-
-                this.clock.setTimeout = function (fn, timeout) {
-                    server.longestTimeout = Math.max(timeout, server.longestTimeout || 0);
-
-                    return clockSetTimeout.apply(this, arguments);
-                };
-
-                this.clock.setInterval = function (fn, timeout) {
-                    server.longestTimeout = Math.max(timeout, server.longestTimeout || 0);
-
-                    return clockSetInterval.apply(this, arguments);
-                };
-            }
-        }
-
-        return sinon.fakeServer.addRequest.call(this, xhr);
-    };
-
-    sinon.fakeServerWithClock.respond = function respond() {
-        var returnVal = sinon.fakeServer.respond.apply(this, arguments);
-
-        if (this.clock) {
-            this.clock.tick(this.longestTimeout || 0);
-            this.longestTimeout = 0;
-
-            if (this.resetClock) {
-                this.clock.restore();
-                this.resetClock = false;
-            }
-        }
-
-        return returnVal;
-    };
-
-    sinon.fakeServerWithClock.restore = function restore() {
-        if (this.clock) {
-            this.clock.restore();
-        }
-
-        return sinon.fakeServer.restore.apply(this, arguments);
-    };
-}());
-
-/**
- * @depend ../sinon.js
- * @depend collection.js
- * @depend util/fake_timers.js
- * @depend util/fake_server_with_clock.js
- */
-/*jslint eqeqeq: false, onevar: false, plusplus: false*/
-/*global require, module*/
-/**
- * Manages fake collections as well as fake utilities such as Sinon's
- * timers and fake XHR implementation in one convenient object.
- *
- * @author Christian Johansen ([email protected])
- * @license BSD
- *
- * Copyright (c) 2010-2011 Christian Johansen
- */
-
-if (typeof module == "object" && typeof require == "function") {
-    var sinon = require("../sinon");
-    sinon.extend(sinon, require("./util/fake_timers"));
-}
-
-(function () {
-    var push = [].push;
-
-    function exposeValue(sandbox, config, key, value) {
-        if (!value) {
-            return;
-        }
-
-        if (config.injectInto) {
-            config.injectInto[key] = value;
-        } else {
-            push.call(sandbox.args, value);
-        }
-    }
-
-    function prepareSandboxFromConfig(config) {
-        var sandbox = sinon.create(sinon.sandbox);
-
-        if (config.useFakeServer) {
-            if (typeof config.useFakeServer == "object") {
-                sandbox.serverPrototype = config.useFakeServer;
-            }
-
-            sandbox.useFakeServer();
-        }
-
-        if (config.useFakeTimers) {
-            if (typeof config.useFakeTimers == "object") {
-                sandbox.useFakeTimers.apply(sandbox, config.useFakeTimers);
-            } else {
-                sandbox.useFakeTimers();
-            }
-        }
-
-        return sandbox;
-    }
-
-    sinon.sandbox = sinon.extend(sinon.create(sinon.collection), {
-        useFakeTimers: function useFakeTimers() {
-            this.clock = sinon.useFakeTimers.apply(sinon, arguments);
-
-            return this.add(this.clock);
-        },
-
-        serverPrototype: sinon.fakeServer,
-
-        useFakeServer: function useFakeServer() {
-            var proto = this.serverPrototype || sinon.fakeServer;
-
-            if (!proto || !proto.create) {
-                return null;
-            }
-
-            this.server = proto.create();
-            return this.add(this.server);
-        },
-
-        inject: function (obj) {
-            sinon.collection.inject.call(this, obj);
-
-            if (this.clock) {
-                obj.clock = this.clock;
-            }
-
-            if (this.server) {
-                obj.server = this.server;
-                obj.requests = this.server.requests;
-            }
-
-            return obj;
-        },
-
-        create: function (config) {
-            if (!config) {
-                return sinon.create(sinon.sandbox);
-            }
-
-            var sandbox = prepareSandboxFromConfig(config);
-            sandbox.args = sandbox.args || [];
-            var prop, value, exposed = sandbox.inject({});
-
-            if (config.properties) {
-                for (var i = 0, l = config.properties.length; i < l; i++) {
-                    prop = config.properties[i];
-                    value = exposed[prop] || prop == "sandbox" && sandbox;
-                    exposeValue(sandbox, config, prop, value);
-                }
-            } else {
-                exposeValue(sandbox, config, "sandbox", value);
-            }
-
-            return sandbox;
-        }
-    });
-
-    sinon.sandbox.useFakeXMLHttpRequest = sinon.sandbox.useFakeServer;
-
-    if (typeof module == "object" && typeof require == "function") {
-        module.exports = sinon.sandbox;
-    }
-}());
-
-/**
- * @depend ../sinon.js
- * @depend stub.js
- * @depend mock.js
- * @depend sandbox.js
- */
-/*jslint eqeqeq: false, onevar: false, forin: true, plusplus: false*/
-/*global module, require, sinon*/
-/**
- * Test function, sandboxes fakes
- *
- * @author Christian Johansen ([email protected])
- * @license BSD
- *
- * Copyright (c) 2010-2011 Christian Johansen
- */
-
-(function (sinon) {
-    var commonJSModule = typeof module == "object" && typeof require == "function";
-
-    if (!sinon && commonJSModule) {
-        sinon = require("../sinon");
-    }
-
-    if (!sinon) {
-        return;
-    }
-
-    function test(callback) {
-        var type = typeof callback;
-
-        if (type != "function") {
-            throw new TypeError("sinon.test needs to wrap a test function, got " + type);
-        }
-
-        return function () {
-            var config = sinon.getConfig(sinon.config);
-            config.injectInto = config.injectIntoThis && this || config.injectInto;
-            var sandbox = sinon.sandbox.create(config);
-            var exception, result;
-            var args = Array.prototype.slice.call(arguments).concat(sandbox.args);
-
-            try {
-                result = callback.apply(this, args);
-            } catch (e) {
-                exception = e;
-            }
-
-            if (typeof exception !== "undefined") {
-                sandbox.restore();
-                throw exception;
-            }
-            else {
-                sandbox.verifyAndRestore();
-            }
-
-            return result;
-        };
-    }
-
-    test.config = {
-        injectIntoThis: true,
-        injectInto: null,
-        properties: ["spy", "stub", "mock", "clock", "server", "requests"],
-        useFakeTimers: true,
-        useFakeServer: true
-    };
-
-    if (commonJSModule) {
-        module.exports = test;
-    } else {
-        sinon.test = test;
-    }
-}(typeof sinon == "object" && sinon || null));
-
-/**
- * @depend ../sinon.js
- * @depend test.js
- */
-/*jslint eqeqeq: false, onevar: false, eqeqeq: false*/
-/*global module, require, sinon*/
-/**
- * Test case, sandboxes all test functions
- *
- * @author Christian Johansen ([email protected])
- * @license BSD
- *
- * Copyright (c) 2010-2011 Christian Johansen
- */
-
-(function (sinon) {
-    var commonJSModule = typeof module == "object" && typeof require == "function";
-
-    if (!sinon && commonJSModule) {
-        sinon = require("../sinon");
-    }
-
-    if (!sinon || !Object.prototype.hasOwnProperty) {
-        return;
-    }
-
-    function createTest(property, setUp, tearDown) {
-        return function () {
-            if (setUp) {
-                setUp.apply(this, arguments);
-            }
-
-            var exception, result;
-
-            try {
-                result = property.apply(this, arguments);
-            } catch (e) {
-                exception = e;
-            }
-
-            if (tearDown) {
-                tearDown.apply(this, arguments);
-            }
-
-            if (exception) {
-                throw exception;
-            }
-
-            return result;
-        };
-    }
-
-    function testCase(tests, prefix) {
-        /*jsl:ignore*/
-        if (!tests || typeof tests != "object") {
-            throw new TypeError("sinon.testCase needs an object with test functions");
-        }
-        /*jsl:end*/
-
-        prefix = prefix || "test";
-        var rPrefix = new RegExp("^" + prefix);
-        var methods = {}, testName, property, method;
-        var setUp = tests.setUp;
-        var tearDown = tests.tearDown;
-
-        for (testName in tests) {
-            if (tests.hasOwnProperty(testName)) {
-                property = tests[testName];
-
-                if (/^(setUp|tearDown)$/.test(testName)) {
-                    continue;
-                }
-
-                if (typeof property == "function" && rPrefix.test(testName)) {
-                    method = property;
-
-                    if (setUp || tearDown) {
-                        method = createTest(property, setUp, tearDown);
-                    }
-
-                    methods[testName] = sinon.test(method);
-                } else {
-                    methods[testName] = tests[testName];
-                }
-            }
-        }
-
-        return methods;
-    }
-
-    if (commonJSModule) {
-        module.exports = testCase;
-    } else {
-        sinon.testCase = testCase;
-    }
-}(typeof sinon == "object" && sinon || null));
-
-/**
- * @depend ../sinon.js
- * @depend stub.js
- */
-/*jslint eqeqeq: false, onevar: false, nomen: false, plusplus: false*/
-/*global module, require, sinon*/
-/**
- * Assertions matching the test spy retrieval interface.
- *
- * @author Christian Johansen ([email protected])
- * @license BSD
- *
- * Copyright (c) 2010-2011 Christian Johansen
- */
-
-(function (sinon, global) {
-    var commonJSModule = typeof module == "object" && typeof require == "function";
-    var slice = Array.prototype.slice;
-    var assert;
-
-    if (!sinon && commonJSModule) {
-        sinon = require("../sinon");
-    }
-
-    if (!sinon) {
-        return;
-    }
-
-    function verifyIsStub() {
-        var method;
-
-        for (var i = 0, l = arguments.length; i < l; ++i) {
-            method = arguments[i];
-
-            if (!method) {
-                assert.fail("fake is not a spy");
-            }
-
-            if (typeof method != "function") {
-                assert.fail(method + " is not a function");
-            }
-
-            if (typeof method.getCall != "function") {
-                assert.fail(method + " is not stubbed");
-            }
-        }
-    }
-
-    function failAssertion(object, msg) {
-        object = object || global;
-        var failMethod = object.fail || assert.fail;
-        failMethod.call(object, msg);
-    }
-
-    function mirrorPropAsAssertion(name, method, message) {
-        if (arguments.length == 2) {
-            message = method;
-            method = name;
-        }
-
-        assert[name] = function (fake) {
-            verifyIsStub(fake);
-
-            var args = slice.call(arguments, 1);
-            var failed = false;
-
-            if (typeof method == "function") {
-                failed = !method(fake);
-            } else {
-                failed = typeof fake[method] == "function" ?
-                    !fake[method].apply(fake, args) : !fake[method];
-            }
-
-            if (failed) {
-                failAssertion(this, fake.printf.apply(fake, [message].concat(args)));
-            } else {
-                assert.pass(name);
-            }
-        };
-    }
-
-    function exposedName(prefix, prop) {
-        return !prefix || /^fail/.test(prop) ? prop :
-            prefix + prop.slice(0, 1).toUpperCase() + prop.slice(1);
-    };
-
-    assert = {
-        failException: "AssertError",
-
-        fail: function fail(message) {
-            var error = new Error(message);
-            error.name = this.failException || assert.failException;
-
-            throw error;
-        },
-
-        pass: function pass(assertion) {},
-
-        callOrder: function assertCallOrder() {
-            verifyIsStub.apply(null, arguments);
-            var expected = "", actual = "";
-
-            if (!sinon.calledInOrder(arguments)) {
-                try {
-                    expected = [].join.call(arguments, ", ");
-                    actual = sinon.orderByFirstCall(slice.call(arguments)).join(", ");
-                } catch (e) {
-                    // If this fails, we'll just fall back to the blank string
-                }
-
-                failAssertion(this, "expected " + expected + " to be " +
-                              "called in order but were called as " + actual);
-            } else {
-                assert.pass("callOrder");
-            }
-        },
-
-        callCount: function assertCallCount(method, count) {
-            verifyIsStub(method);
-
-            if (method.callCount != count) {
-                var msg = "expected %n to be called " + sinon.timesInWords(count) +
-                    " but was called %c%C";
-                failAssertion(this, method.printf(msg));
-            } else {
-                assert.pass("callCount");
-            }
-        },
-
-        expose: function expose(target, options) {
-            if (!target) {
-                throw new TypeError("target is null or undefined");
-            }
-
-            var o = options || {};
-            var prefix = typeof o.prefix == "undefined" && "assert" || o.prefix;
-            var includeFail = typeof o.includeFail == "undefined" || !!o.includeFail;
-
-            for (var method in this) {
-                if (method != "export" && (includeFail || !/^(fail)/.test(method))) {
-                    target[exposedName(prefix, method)] = this[method];
-                }
-            }
-
-            return target;
-        }
-    };
-
-    mirrorPropAsAssertion("called", "expected %n to have been called at least once but was never called");
-    mirrorPropAsAssertion("notCalled", function (spy) { return !spy.called; },
-                          "expected %n to not have been called but was called %c%C");
-    mirrorPropAsAssertion("calledOnce", "expected %n to be called once but was called %c%C");
-    mirrorPropAsAssertion("calledTwice", "expected %n to be called twice but was called %c%C");
-    mirrorPropAsAssertion("calledThrice", "expected %n to be called thrice but was called %c%C");
-    mirrorPropAsAssertion("calledOn", "expected %n to be called with %1 as this but was called with %t");
-    mirrorPropAsAssertion("alwaysCalledOn", "expected %n to always be called with %1 as this but was called with %t");
-    mirrorPropAsAssertion("calledWithNew", "expected %n to be called with new");
-    mirrorPropAsAssertion("alwaysCalledWithNew", "expected %n to always be called with new");
-    mirrorPropAsAssertion("calledWith", "expected %n to be called with arguments %*%C");
-    mirrorPropAsAssertion("calledWithMatch", "expected %n to be called with match %*%C");
-    mirrorPropAsAssertion("alwaysCalledWith", "expected %n to always be called with arguments %*%C");
-    mirrorPropAsAssertion("alwaysCalledWithMatch", "expected %n to always be called with match %*%C");
-    mirrorPropAsAssertion("calledWithExactly", "expected %n to be called with exact arguments %*%C");
-    mirrorPropAsAssertion("alwaysCalledWithExactly", "expected %n to always be called with exact arguments %*%C");
-    mirrorPropAsAssertion("neverCalledWith", "expected %n to never be called with arguments %*%C");
-    mirrorPropAsAssertion("neverCalledWithMatch", "expected %n to never be called with match %*%C");
-    mirrorPropAsAssertion("threw", "%n did not throw exception%C");
-    mirrorPropAsAssertion("alwaysThrew", "%n did not always throw exception%C");
-
-    if (commonJSModule) {
-        module.exports = assert;
-    } else {
-        sinon.assert = assert;
-    }
-}(typeof sinon == "object" && sinon || null, typeof window != "undefined" ? window : global));
-
-return sinon;}.call(typeof window != 'undefined' && window || {}));
\ No newline at end of file
diff --git a/dep/django-selectable/selectable/tests/qunit/test-events.js b/dep/django-selectable/selectable/tests/qunit/test-events.js
deleted file mode 100644 (file)
index f7b3ffe..0000000
+++ /dev/null
@@ -1,221 +0,0 @@
-/*global define, module, test, expect, equal, ok*/
-
-define(['selectable'], function ($) {
-
-    module("Basic Event Tests", {
-        setup: function () {
-            // Patch AJAX requests
-            var self = this;
-            this.xhr = sinon.useFakeXMLHttpRequest();
-            this.requests = [];
-            this.xhr.onCreate = function (xhr) {
-                self.requests.push(xhr);
-            };
-            this.inputs = createTextSelect('autocompleteselect');
-            this.textInput = this.inputs[0];
-            this.hiddenInput = this.inputs[1];
-            $('#qunit-fixture').append(this.textInput);
-            $('#qunit-fixture').append(this.hiddenInput);
-            bindSelectables('#qunit-fixture');
-        },
-        teardown: function () {
-            this.xhr.restore();
-            this.textInput.djselectable('destroy');
-        }
-    });
-
-    test("Manual Selection", function() {
-        expect(1);
-        var count = 0,
-            item = {id: "1", value: 'foo'};
-        this.textInput.bind('djselectableselect', function(e, item) {
-            count = count + 1;
-        });
-        var item = {id: "1", value: 'foo'};
-        this.textInput.djselectable('select', item);
-        equal(count, 1, "djselectableselect should fire once when manually selected.");
-    });
-
-    test("Manual Selection with Double Bind", function() {
-        expect(1);
-        var count = 0,
-            item = {id: "1", value: 'foo'};
-        bindSelectables('#qunit-fixture');
-        this.textInput.bind('djselectableselect', function(e, item) {
-            count = count + 1;
-        });
-        this.textInput.djselectable('select', item);
-        equal(count, 1, "djselectableselect should fire once when manually selected.");
-    });
-
-    test("Menu Selection", function() {
-        expect(2);
-        var count = 0,
-            down = jQuery.Event("keydown"),
-            enter = jQuery.Event("keydown"),
-            response = simpleLookupResponse(),
-            self = this;
-        down.keyCode = $.ui.keyCode.DOWN;
-        enter.keyCode = $.ui.keyCode.ENTER;
-        this.textInput.bind('djselectableselect', function(e, item) {
-            count = count + 1;
-        });
-        this.textInput.val("ap").keydown();
-        stop();
-        setTimeout(function () {
-            equal(self.requests.length, 1, "AJAX request should be triggered.");
-            self.requests[0].respond(200, {"Content-Type": "application/json"},
-                JSON.stringify(response)
-            );
-            self.textInput.trigger(down);
-            self.textInput.trigger(enter);
-            equal(count, 1, "djselectableselect should only fire once.");
-            start();
-        }, 300);
-    });
-
-    test("Pagination Click", function() {
-        expect(3);
-        var count = 0,
-            response = paginatedLookupResponse(),
-            self = this;
-        this.textInput.bind('djselectableselect', function(e, item) {
-            count = count + 1;
-        });
-        this.textInput.val("ap").keydown();
-        stop();
-        setTimeout(function () {
-            equal(self.requests.length, 1, "AJAX request should be triggered.");
-            self.requests[0].respond(200, {"Content-Type": "application/json"},
-                JSON.stringify(response)
-            );
-            $('.ui-menu-item.selectable-paginator:visible a').trigger("mouseenter");
-            $('.ui-menu-item.selectable-paginator:visible a').trigger("click");
-            equal(self.requests.length, 2, "Another AJAX request should be triggered.");
-            equal(count, 0, "djselectableselect should not fire for new page.");
-            start();
-        }, 300);
-    });
-
-    test("Pagination Enter", function() {
-        expect(3);
-        var count = 0,
-            down = jQuery.Event("keydown"),
-            enter = jQuery.Event("keydown"),
-            response = paginatedLookupResponse(),
-            self = this;
-        down.keyCode = $.ui.keyCode.DOWN;
-        enter.keyCode = $.ui.keyCode.ENTER;
-        this.textInput.bind('djselectableselect', function(e, item) {
-            count = count + 1;
-        });
-        this.textInput.val("ap").keydown();
-        stop();
-        setTimeout(function () {
-            equal(self.requests.length, 1, "AJAX request should be triggered.");
-            self.requests[0].respond(200, {"Content-Type": "application/json"},
-                JSON.stringify(response)
-            );
-            self.textInput.trigger(down);
-            self.textInput.trigger(down);
-            self.textInput.trigger(down);
-            self.textInput.trigger(enter);
-            equal(self.requests.length, 2, "Another AJAX request should be triggered.");
-            equal(count, 0, "djselectableselect should not fire for new page.");
-            start();
-        }, 300);
-    });
-
-    test("Pagination Render", function() {
-        expect(2);
-        var count = 0,
-            down = jQuery.Event("keydown"),
-            enter = jQuery.Event("keydown"),
-            response = paginatedLookupResponse(),
-            self = this;
-        down.keyCode = $.ui.keyCode.DOWN;
-        enter.keyCode = $.ui.keyCode.ENTER;
-        this.textInput.val("ap").keydown();
-        stop();
-        setTimeout(function () {
-            var options;
-            self.requests[0].respond(200, {"Content-Type": "application/json"},
-                JSON.stringify(response)
-            );
-            options = $('li.ui-menu-item:visible');
-            equal(options.length, 3, "Currently 3 menu items.");
-            // $('.selectable-paginator:visible').click();
-            self.textInput.trigger(down);
-            self.textInput.trigger(down);
-            self.textInput.trigger(down);
-            self.textInput.trigger(enter);
-            self.requests[1].respond(200, {"Content-Type": "application/json"},
-                JSON.stringify(response)
-            );
-            options = $('li.ui-menu-item:visible');
-            equal(options.length, 5, "Now 5 menu items.");
-            start();
-        }, 300);
-    });
-
-    module("Custom Event Tests", {
-        setup: function () {
-            this.inputs = createTextSelectMultiple('autocompleteselectmultiple');
-            this.textInput = this.inputs[0];
-            this.hiddenInput = this.inputs[1];
-            $('#qunit-fixture').append(this.textInput);
-            bindSelectables('#qunit-fixture');
-        }
-    });
-
-    test("Add Deck Item", function() {
-        expect(1);
-        var count = 0,
-            item = {id: "1", value: 'foo'};
-        this.textInput.bind('djselectableadd', function(e, item) {
-            count = count + 1;
-        });
-        this.textInput.djselectable('select', item);
-        equal(count, 1, "djselectableadd should fire once when manually selected.");
-    });
-
-    test("Prevent Add Deck Item", function() {
-        expect(1);
-        var count = 0,
-            item = {id: "1", value: 'foo'},
-            deck = $('.selectable-deck', '#qunit-fixture');
-        this.textInput.bind('djselectableadd', function(e, item) {
-            return false;
-        });
-        this.textInput.djselectable('select', item);
-        deck = $('.selectable-deck', '#qunit-fixture');
-        equal($('li', deck).length, 0, "Item should not be added.");
-    });
-
-    test("Remove Deck Item", function() {
-        expect(1);
-        var count = 0,
-            item = {id: "1", value: 'foo'},
-            deck = $('.selectable-deck', '#qunit-fixture');
-        this.textInput.bind('djselectableremove', function(e, item) {
-            count = count + 1;
-        });
-        this.textInput.djselectable('select', item);
-        $('.selectable-deck-remove', deck).click();
-        equal(count, 1, "djselectableremove should fire once when item is removed.");
-    });
-
-    test("Prevent Remove Deck Item", function() {
-        expect(1);
-        var count = 0,
-            item = {id: "1", value: 'foo'},
-            deck = $('.selectable-deck', '#qunit-fixture');
-        this.textInput.bind('djselectableremove', function(e, item) {
-            return false;
-        });
-        var item = {id: "1", value: 'foo'};
-        this.textInput.djselectable('select', item);
-        $('.selectable-deck-remove', deck).click();
-        equal($('li', deck).length, 1, "Item should not be removed.");
-    });
-});
\ No newline at end of file
diff --git a/dep/django-selectable/selectable/tests/qunit/test-methods.js b/dep/django-selectable/selectable/tests/qunit/test-methods.js
deleted file mode 100644 (file)
index 6720dd4..0000000
+++ /dev/null
@@ -1,268 +0,0 @@
-/*global define, module, test, expect, equal, ok*/
-
-define(['selectable'], function ($) {
-
-    var expectedNamespace = 'djselectable',
-        useData = true;
-    if (window.uiversion.lastIndexOf('1.10', 0) === 0) {
-        // jQuery UI 1.10 introduces a namespace change to include ui-prefix
-        expectedNamespace = 'ui-' + expectedNamespace;
-    }
-    if (window.uiversion.lastIndexOf('1.11', 0) === 0) {
-        // jQuery UI 1.11 introduces an instance method to get the current instance
-        useData = false;
-    }
-
-    module("Autocomplete Text Methods Tests");
-
-    test("Bind Input", function () {
-        expect(2);
-        var input = createTextComplete('autocomplete');
-        $('#qunit-fixture').append(input);
-        bindSelectables('#qunit-fixture');
-        ok(input.hasClass('ui-autocomplete-input'), "input should be bound with djselecable widget");
-        if (useData) {
-            ok(input.data(expectedNamespace), "input should be bound with djselecable widget");
-        } else {
-            ok(input.djselectable('instance'), "input should be bound with djselecable widget");
-        }
-    });
-
-    test("Manual Selection", function () {
-        expect(1);
-        var item = {id: "1", value: 'foo'},
-            input = createTextComplete('autocomplete');
-        $('#qunit-fixture').append(input);
-        bindSelectables('#qunit-fixture');
-        input.djselectable('select', item);
-        equal(input.val(), item.value, "input should get item value");
-    });
-
-    test("Initial Data", function () {
-        expect(1);
-        var input = createTextComplete('autocomplete');
-        input.val('Foo');
-        $('#qunit-fixture').append(input);
-        bindSelectables('#qunit-fixture');
-        equal(input.val(), 'Foo', "initial text value should not be lost");
-    });
-
-
-    module("Autocombobox Text Methods Tests");
-
-    test("Bind Input", function () {
-        expect(3);
-        var input = createTextCombobox('autocombobox'), button;
-        $('#qunit-fixture').append(input);
-        bindSelectables('#qunit-fixture');
-        button = $('.ui-combo-button', '#qunit-fixture');
-        ok(input.hasClass('ui-autocomplete-input'), "input should be bound with djselecable widget");
-        if (useData) {
-            ok(input.data(expectedNamespace), "input should be bound with djselecable widget");
-        } else {
-            ok(input.djselectable('instance'), "input should be bound with djselecable widget");
-        }
-        equal(button.length, 1, "combobox button should be created");
-    });
-
-    test("Manual Selection", function () {
-        expect(1);
-        var item = {id: "1", value: 'foo'},
-            input = createTextCombobox('autocombobox');
-        $('#qunit-fixture').append(input);
-        bindSelectables('#qunit-fixture');
-        input.djselectable('select', item);
-        equal(input.val(), item.value, "input should get item value");
-    });
-
-    test("Initial Data", function () {
-        expect(1);
-        var input = createTextCombobox('autocombobox');
-        input.val('Foo');
-        $('#qunit-fixture').append(input);
-        bindSelectables('#qunit-fixture');
-        equal(input.val(), 'Foo', "initial text value should not be lost");
-    });
-
-    module("Autocomplete Select Methods Tests");
-
-    test("Bind Input", function () {
-        expect(2);
-        var inputs = createTextSelect('autocompleteselect'),
-            textInput = inputs[0], hiddenInput = inputs[1];
-        $('#qunit-fixture').append(textInput);
-        $('#qunit-fixture').append(hiddenInput);
-        bindSelectables('#qunit-fixture');
-        ok(textInput.hasClass('ui-autocomplete-input'), "input should be bound with djselecable widget");
-        if (useData) {
-            ok(textInput.data(expectedNamespace), "input should be bound with djselecable widget");
-        } else {
-            ok(textInput.djselectable('instance'), "input should be bound with djselecable widget");
-        }
-    });
-
-    test("Manual Selection", function () {
-        expect(2);
-        var item = {id: "1", value: 'foo'},
-            inputs = createTextSelect('autocompleteselect'),
-            textInput = inputs[0], hiddenInput = inputs[1];
-        $('#qunit-fixture').append(textInput);
-        $('#qunit-fixture').append(hiddenInput);
-        bindSelectables('#qunit-fixture');
-        textInput.djselectable('select', item);
-        equal(textInput.val(), item.value, "input should get item value");
-        equal(hiddenInput.val(), item.id, "input should get item id");
-    });
-
-    test("Initial Data", function () {
-        expect(2);
-        var inputs = createTextSelect('autocompleteselect'),
-            textInput = inputs[0], hiddenInput = inputs[1];
-        $('#qunit-fixture').append(textInput);
-        $('#qunit-fixture').append(hiddenInput);
-        textInput.val('Foo');
-        hiddenInput.val('1');
-        bindSelectables('#qunit-fixture');
-        equal(textInput.val(), 'Foo', "initial text value should not be lost");
-        equal(hiddenInput.val(), '1', "initial pk value should not be lost");
-    });
-
-    module("Autocombobox Select Methods Tests");
-
-    test("Bind Input", function () {
-        expect(3);
-        var inputs = createComboboxSelect('autocomboboxselect'),
-            textInput = inputs[0], hiddenInput = inputs[1], button;
-        $('#qunit-fixture').append(textInput);
-        $('#qunit-fixture').append(hiddenInput);
-        bindSelectables('#qunit-fixture');
-        button = $('.ui-combo-button', '#qunit-fixture');
-        ok(textInput.hasClass('ui-autocomplete-input'), "input should be bound with djselecable widget");
-        if (useData) {
-            ok(textInput.data(expectedNamespace), "input should be bound with djselecable widget");
-        } else {
-            ok(textInput.djselectable('instance'), "input should be bound with djselecable widget");
-        }
-        equal(button.length, 1, "combobox button should be created");
-    });
-
-    test("Manual Selection", function () {
-        expect(2);
-        var item = {id: "1", value: 'foo'},
-            inputs = createComboboxSelect('autocomboboxselect'),
-            textInput = inputs[0], hiddenInput = inputs[1];
-        $('#qunit-fixture').append(textInput);
-        $('#qunit-fixture').append(hiddenInput);
-        bindSelectables('#qunit-fixture');
-        textInput.djselectable('select', item);
-        equal(textInput.val(), item.value, "input should get item value");
-        equal(hiddenInput.val(), item.id, "input should get item id");
-    });
-
-    test("Initial Data", function () {
-        expect(2);
-        var inputs = createComboboxSelect('autocomboboxselect'),
-            textInput = inputs[0], hiddenInput = inputs[1];
-        $('#qunit-fixture').append(textInput);
-        $('#qunit-fixture').append(hiddenInput);
-        textInput.val('Foo');
-        hiddenInput.val('1');
-        bindSelectables('#qunit-fixture');
-        equal(textInput.val(), 'Foo', "initial text value should not be lost");
-        equal(hiddenInput.val(), '1', "initial pk value should not be lost");
-    });
-
-    module("Autocomplete Select Multiple Methods Tests");
-
-    test("Bind Input", function () {
-        expect(3);
-        var inputs = createTextSelectMultiple('autocompletemultiple'),
-            textInput = inputs[0], deck;
-        $('#qunit-fixture').append(textInput);
-        bindSelectables('#qunit-fixture');
-        deck = $('.selectable-deck', '#qunit-fixture');
-        ok(textInput.hasClass('ui-autocomplete-input'), "input should be bound with djselecable widget");
-        if (useData) {
-            ok(textInput.data(expectedNamespace), "input should be bound with djselecable widget");
-        } else {
-            ok(textInput.djselectable('instance'), "input should be bound with djselecable widget");
-        }
-        equal($('li', deck).length, 0, "no initial deck items");
-    });
-
-    test("Manual Selection", function () {
-        expect(2);
-        var item = {id: "1", value: 'foo'},
-            inputs = createTextSelectMultiple('autocompletemultiple'),
-            textInput = inputs[0], hiddenInput;
-        $('#qunit-fixture').append(textInput);
-        bindSelectables('#qunit-fixture');
-        textInput.djselectable('select', item);
-        hiddenInput = $(':input[type=hidden][name=autocompletemultiple_1]', '#qunit-fixture');
-        equal(textInput.val(), '', "input should be empty");
-        equal(hiddenInput.val(), item.id, "input should get item id");
-    });
-
-    test("Initial Data", function () {
-        expect(3);
-        var inputs = createTextSelectMultiple('autocomboboxselect'),
-            textInput = inputs[0], hiddenInput = inputs[1], deck;
-        $('#qunit-fixture').append(textInput);
-        $('#qunit-fixture').append(hiddenInput);
-        textInput.val('Foo');
-        hiddenInput.val('1');
-        bindSelectables('#qunit-fixture');
-        deck = $('.selectable-deck', '#qunit-fixture');
-        equal(textInput.val(), '', "input should be empty");
-        equal(hiddenInput.val(), '1', "initial pk value should not be lost");
-        equal($('li', deck).length, 1, "one initial deck items");
-    });
-
-    module("Autocombobox Select Multiple Methods Tests");
-
-    test("Bind Input", function () {
-        expect(4);
-        var inputs = createComboboxSelectMultiple('autocomboboxmultiple'),
-            textInput = inputs[0], deck, button;
-        $('#qunit-fixture').append(textInput);
-        bindSelectables('#qunit-fixture');
-        deck = $('.selectable-deck', '#qunit-fixture');
-        button = $('.ui-combo-button', '#qunit-fixture');
-        ok(textInput.hasClass('ui-autocomplete-input'), "input should be bound with djselecable widget");
-        if (useData) {
-            ok(textInput.data(expectedNamespace), "input should be bound with djselecable widget");
-        } else {
-            ok(textInput.djselectable('instance'), "input should be bound with djselecable widget");
-        }
-        equal($('li', deck).length, 0, "no initial deck items");
-        equal(button.length, 1, "combobox button should be created");
-    });
-
-    test("Manual Selection", function () {
-        expect(2);
-        var item = {id: "1", value: 'foo'},
-            inputs = createComboboxSelectMultiple('autocomboboxmultiple'),
-            textInput = inputs[0], hiddenInput;
-        $('#qunit-fixture').append(textInput);
-        bindSelectables('#qunit-fixture');
-        textInput.djselectable('select', item);
-        hiddenInput = $(':input[type=hidden][name=autocomboboxmultiple_1]', '#qunit-fixture');
-        equal(textInput.val(), '', "input should be empty");
-        equal(hiddenInput.val(), item.id, "input should get item id");
-    });
-
-    test("Initial Data", function () {
-        expect(3);
-        var inputs = createComboboxSelectMultiple('autocomboboxmultiple'),
-            textInput = inputs[0], hiddenInput = inputs[1], deck;
-        $('#qunit-fixture').append(textInput);
-        $('#qunit-fixture').append(hiddenInput);
-        textInput.val('Foo');
-        hiddenInput.val('1');
-        bindSelectables('#qunit-fixture');
-        deck = $('.selectable-deck', '#qunit-fixture');
-        equal(textInput.val(), '', "input should be empty");
-        equal(hiddenInput.val(), '1', "initial pk value should not be lost");
-        equal($('li', deck).length, 1, "one initial deck items");
-    });
-});
\ No newline at end of file
diff --git a/dep/django-selectable/selectable/tests/qunit/test-options.js b/dep/django-selectable/selectable/tests/qunit/test-options.js
deleted file mode 100644 (file)
index 89c1fff..0000000
+++ /dev/null
@@ -1,174 +0,0 @@
-/*global define, module, test, expect, equal, ok*/
-
-define(['selectable'], function ($) {
-
-    module("Plugin Options Tests", {
-        setup: function () {
-            // Patch AJAX requests
-            var self = this;
-            this.xhr = sinon.useFakeXMLHttpRequest();
-            this.requests = [];
-            this.xhr.onCreate = function (xhr) {
-                self.requests.push(xhr);
-            };
-            this.input = createTextComplete('autocomplete');
-            $('#qunit-fixture').append(this.input);
-            bindSelectables('#qunit-fixture');
-        },
-        teardown: function () {
-            this.xhr.restore();
-            this.input.djselectable('destroy');
-        }
-    });
-
-    test("Highlight Match On", function () {
-        expect(2);
-        var response = simpleLookupResponse(),
-            self = this,
-            menu, item, highlight;
-        this.input.djselectable("option", "highlightMatch", true);
-        this.input.val("ap").keydown();
-        stop();
-        setTimeout(function () {
-            self.requests[0].respond(200, {"Content-Type": "application/json"},
-                JSON.stringify(response)
-            );
-            menu = $('ul.ui-autocomplete.ui-menu:visible');
-            item = $('li', menu).eq(0);
-            highlight = $('.highlight', item);
-            equal(highlight.length, 1, "Highlight should be present");
-            equal(highlight.text(), "Ap", "Highlight text should match");
-            start();
-        }, 300);
-    });
-
-    test("Highlight Match Off", function () {
-        expect(1);
-        var response = simpleLookupResponse(),
-            self = this,
-            menu, item, highlight;
-        this.input.djselectable("option", "highlightMatch", false);
-        this.input.val("ap").keydown();
-        stop();
-        setTimeout(function () {
-            self.requests[0].respond(200, {"Content-Type": "application/json"},
-                JSON.stringify(response)
-            );
-            menu = $('ul.ui-autocomplete.ui-menu:visible');
-            item = $('li', menu).eq(0);
-            highlight = $('.highlight', item);
-            equal(highlight.length, 0, "Highlight should not be present");
-            start();
-        }, 300);
-    });
-
-    test("Format Label String (No Highlight)", function () {
-        expect(3);
-        var response = simpleLookupResponse(),
-            self = this,
-            menu, item, custom, highlight;
-        function customFormat(label, item) {
-            return "<span class='custom'>" +  label + "</span>";
-        }
-        this.input.djselectable("option", "formatLabel", customFormat);
-        this.input.djselectable("option", "highlightMatch", false);
-        this.input.val("ap").keydown();
-        stop();
-        setTimeout(function () {
-            self.requests[0].respond(200, {"Content-Type": "application/json"},
-                JSON.stringify(response)
-            );
-            menu = $('ul.ui-autocomplete.ui-menu:visible');
-            item = $('li', menu).eq(0);
-            custom = $('.custom', item);
-            equal(custom.length, 1, "Custom label should be present");
-            equal(custom.text(), "Apple", "Label text should match");
-            highlight = $('.highlight', item);
-            equal(highlight.length, 0, "Highlight should not be present");
-            start();
-        }, 300);
-    });
-
-    test("Format Label jQuery Object (No Highlight)", function () {
-        expect(3);
-        var response = simpleLookupResponse(),
-            self = this,
-            menu, item, custom, highlight
-        function customFormat(label, item) {
-            return $("<span>").addClass("custom").text(label);
-        }
-        this.input.djselectable("option", "formatLabel", customFormat);
-        this.input.djselectable("option", "highlightMatch", false);
-        this.input.val("ap").keydown();
-        stop();
-        setTimeout(function () {
-            self.requests[0].respond(200, {"Content-Type": "application/json"},
-                JSON.stringify(response)
-            );
-            menu = $('ul.ui-autocomplete.ui-menu:visible');
-            item = $('li', menu).eq(0);
-            custom = $('.custom', item);
-            equal(custom.length, 1, "Custom label should be present");
-            equal(custom.text(), "Apple", "Label text should match");
-            highlight = $('.highlight', item);
-            equal(highlight.length, 0, "Highlight should not be present");
-            start();
-        }, 300);
-    });
-
-    test("Format Label String (With Highlight)", function () {
-        expect(4);
-        var response = simpleLookupResponse(),
-            self = this,
-            menu, item, custom, highlight;
-        function customFormat(label, item) {
-            return "<span class='custom'>" +  label + "</span>";
-        }
-        this.input.djselectable("option", "formatLabel", customFormat);
-        this.input.djselectable("option", "highlightMatch", true);
-        this.input.val("ap").keydown();
-        stop();
-        setTimeout(function () {
-            self.requests[0].respond(200, {"Content-Type": "application/json"},
-                JSON.stringify(response)
-            );
-            menu = $('ul.ui-autocomplete.ui-menu:visible');
-            item = $('li', menu).eq(0);
-            custom = $('.custom', item);
-            equal(custom.length, 1, "Custom label should be present");
-            equal(custom.text(), "Apple", "Label text should match");
-            highlight = $('.highlight', custom);
-            equal(highlight.length, 1, "Highlight should be present");
-            equal(highlight.text(), "Ap", "Highlight text should match");
-            start();
-        }, 300);
-    });
-
-    test("Format Label jQuery Object (With Highlight)", function () {
-        expect(4);
-        var response = simpleLookupResponse(),
-            self = this,
-            menu, item, custom, highlight;
-        function customFormat(label, item) {
-            return $("<span>").addClass("custom").text(label);
-        }
-        this.input.djselectable("option", "formatLabel", customFormat);
-        this.input.djselectable("option", "highlightMatch", true);
-        this.input.val("ap").keydown();
-        stop();
-        setTimeout(function () {
-            self.requests[0].respond(200, {"Content-Type": "application/json"},
-                JSON.stringify(response)
-            );
-            menu = $('ul.ui-autocomplete.ui-menu:visible');
-            item = $('li', menu).eq(0);
-            custom = $('.custom', item);
-            equal(custom.length, 1, "Custom label should be present");
-            equal(custom.text(), "Apple", "Label text should match");
-            highlight = $('.highlight', custom);
-            equal(highlight.length, 1, "Highlight should be present");
-            equal(highlight.text(), "Ap", "Highlight text should match");
-            start();
-        }, 300);
-    });
-});
\ No newline at end of file
diff --git a/dep/django-selectable/selectable/tests/test_base.py b/dep/django-selectable/selectable/tests/test_base.py
deleted file mode 100644 (file)
index b6b2bb9..0000000
+++ /dev/null
@@ -1,131 +0,0 @@
-from __future__ import unicode_literals
-
-from django.urls import reverse
-from django.utils.html import escape
-from django.utils.safestring import SafeData, mark_safe
-
-from ..base import ModelLookup
-from . import Thing
-from .base import BaseSelectableTestCase, SimpleModelLookup
-
-__all__ = (
-    'ModelLookupTestCase',
-    'MultiFieldLookupTestCase',
-    'LookupEscapingTestCase',
-)
-
-
-class ModelLookupTestCase(BaseSelectableTestCase):
-    lookup_cls = SimpleModelLookup
-
-    def get_lookup_instance(self):
-        return self.__class__.lookup_cls()
-
-    def test_get_name(self):
-        name = self.__class__.lookup_cls.name()
-        self.assertEqual(name, 'tests-simplemodellookup')
-
-    def test_get_url(self):
-        url = self.__class__.lookup_cls.url()
-        test_url = reverse('selectable-lookup', args=['tests-simplemodellookup'])
-        self.assertEqual(url, test_url)
-
-    def test_format_item(self):
-        lookup = self.get_lookup_instance()
-        thing = Thing()
-        item_info = lookup.format_item(thing)
-        self.assertTrue('id' in item_info)
-        self.assertTrue('value' in item_info)
-        self.assertTrue('label' in item_info)
-
-    def test_get_query(self):
-        lookup = self.get_lookup_instance()
-        thing = self.create_thing(data={'name': 'Thing'})
-        other_thing = self.create_thing(data={'name': 'Other Thing'})
-        qs = lookup.get_query(request=None, term='other')
-        self.assertTrue(thing.pk not in qs.values_list('id', flat=True))
-        self.assertTrue(other_thing.pk in qs.values_list('id', flat=True))
-
-    def test_create_item(self):
-        value = self.get_random_string()
-        lookup = self.get_lookup_instance()
-        thing = lookup.create_item(value)
-        self.assertEqual(thing.__class__, Thing)
-        self.assertEqual(thing.name, value)
-        self.assertFalse(thing.pk)
-
-    def test_get_item(self):
-        lookup = self.get_lookup_instance()
-        thing = self.create_thing(data={'name': 'Thing'})
-        item = lookup.get_item(thing.pk)
-        self.assertEqual(thing, item)
-
-    def test_format_item_escaping(self):
-        "Id, value and label should be escaped."
-        lookup = self.get_lookup_instance()
-        thing = self.create_thing(data={'name': 'Thing'})
-        item_info = lookup.format_item(thing)
-        self.assertFalse(isinstance(item_info['id'], SafeData))
-        self.assertFalse(isinstance(item_info['value'], SafeData))
-        self.assertTrue(isinstance(item_info['label'], SafeData))
-
-
-class MultiFieldLookup(ModelLookup):
-    model = Thing
-    search_fields = ('name__icontains', 'description__icontains', )
-
-
-class MultiFieldLookupTestCase(ModelLookupTestCase):
-    lookup_cls = MultiFieldLookup
-
-    def test_get_name(self):
-        name = self.__class__.lookup_cls.name()
-        self.assertEqual(name, 'tests-multifieldlookup')
-
-    def test_get_url(self):
-        url = self.__class__.lookup_cls.url()
-        test_url = reverse('selectable-lookup', args=['tests-multifieldlookup'])
-        self.assertEqual(url, test_url)
-
-    def test_description_search(self):
-        lookup = self.get_lookup_instance()
-        thing = self.create_thing(data={'description': 'Thing'})
-        other_thing = self.create_thing(data={'description': 'Other Thing'})
-        qs = lookup.get_query(request=None, term='other')
-        self.assertTrue(thing.pk not in qs.values_list('id', flat=True))
-        self.assertTrue(other_thing.pk in qs.values_list('id', flat=True))
-
-
-class HTMLLookup(ModelLookup):
-    model = Thing
-    search_fields = ('name__icontains', )
-
-
-class SafeHTMLLookup(ModelLookup):
-    model = Thing
-    search_fields = ('name__icontains', )
-
-    def get_item_label(self, item):
-        "Mark label as safe."
-        return mark_safe(item.name)
-
-
-class LookupEscapingTestCase(BaseSelectableTestCase):
-
-    def test_escape_html(self):
-        "HTML should be escaped by default."
-        lookup = HTMLLookup()
-        bad_name = "<script>alert('hacked');</script>"
-        escaped_name = escape(bad_name)
-        thing = self.create_thing(data={'name': bad_name})
-        item_info = lookup.format_item(thing)
-        self.assertEqual(item_info['label'], escaped_name)
-
-    def test_conditional_escape(self):
-        "Methods should be able to mark values as safe."
-        lookup = SafeHTMLLookup()
-        bad_name = "<script>alert('hacked');</script>"
-        escaped_name = escape(bad_name)
-        thing = self.create_thing(data={'name': bad_name})
-        item_info = lookup.format_item(thing)
-        self.assertEqual(item_info['label'], bad_name)
diff --git a/dep/django-selectable/selectable/tests/test_decorators.py b/dep/django-selectable/selectable/tests/test_decorators.py
deleted file mode 100644 (file)
index 2a7ef0e..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-try:
-    from unittest.mock import Mock
-except ImportError:
-    from mock import Mock
-
-from ..decorators import ajax_required, login_required, staff_member_required
-from .base import BaseSelectableTestCase, SimpleModelLookup
-
-
-__all__ = (
-    'AjaxRequiredLookupTestCase',
-    'LoginRequiredLookupTestCase',
-    'StaffRequiredLookupTestCase',
-)
-
-
-class AjaxRequiredLookupTestCase(BaseSelectableTestCase):
-
-    def setUp(self):
-        self.lookup = ajax_required(SimpleModelLookup)()
-
-    def test_ajax_call(self):
-        "Ajax call should yield a successful response."
-        request = Mock()
-        request.is_ajax = lambda: True
-        response = self.lookup.results(request)
-        self.assertTrue(response.status_code, 200)
-
-    def test_non_ajax_call(self):
-        "Non-Ajax call should yield a bad request response."
-        request = Mock()
-        request.is_ajax = lambda: False
-        response = self.lookup.results(request)
-        self.assertEqual(response.status_code, 400)
-
-
-class LoginRequiredLookupTestCase(BaseSelectableTestCase):
-
-    def setUp(self):
-        self.lookup = login_required(SimpleModelLookup)()
-
-    def test_authenicated_call(self):
-        "Authenicated call should yield a successful response."
-        request = Mock()
-        user = Mock()
-        user.is_authenticated = True
-        request.user = user
-        response = self.lookup.results(request)
-        self.assertTrue(response.status_code, 200)
-
-    def test_non_authenicated_call(self):
-        "Non-Authenicated call should yield an unauthorized response."
-        request = Mock()
-        user = Mock()
-        user.is_authenticated = False
-        request.user = user
-        response = self.lookup.results(request)
-        self.assertEqual(response.status_code, 401)
-
-
-class StaffRequiredLookupTestCase(BaseSelectableTestCase):
-
-    def setUp(self):
-        self.lookup = staff_member_required(SimpleModelLookup)()
-
-    def test_staff_member_call(self):
-        "Staff member call should yield a successful response."
-        request = Mock()
-        user = Mock()
-        user.is_authenticated = True
-        user.is_staff = True
-        request.user = user
-        response = self.lookup.results(request)
-        self.assertTrue(response.status_code, 200)
-
-    def test_authenicated_but_not_staff(self):
-        "Authenicated but non staff call should yield a forbidden response."
-        request = Mock()
-        user = Mock()
-        user.is_authenticated = True
-        user.is_staff = False
-        request.user = user
-        response = self.lookup.results(request)
-        self.assertTrue(response.status_code, 403)
-
-    def test_non_authenicated_call(self):
-        "Non-Authenicated call should yield an unauthorized response."
-        request = Mock()
-        user = Mock()
-        user.is_authenticated = False
-        user.is_staff = False
-        request.user = user
-        response = self.lookup.results(request)
-        self.assertEqual(response.status_code, 401)
diff --git a/dep/django-selectable/selectable/tests/test_fields.py b/dep/django-selectable/selectable/tests/test_fields.py
deleted file mode 100644 (file)
index 24d12cb..0000000
+++ /dev/null
@@ -1,134 +0,0 @@
-from django import forms
-
-from selectable.forms import fields, widgets
-from selectable.tests import ThingLookup
-from selectable.tests.base import BaseSelectableTestCase
-
-
-__all__ = (
-    'AutoCompleteSelectFieldTestCase',
-    'AutoCompleteSelectMultipleFieldTestCase',
-)
-
-class FieldTestMixin(object):
-    field_cls = None
-    lookup_cls = None
-
-    def get_field_instance(self, allow_new=False, limit=None, widget=None):
-        return self.field_cls(self.lookup_cls, allow_new=allow_new, limit=limit, widget=widget)
-
-    def test_init(self):
-        field = self.get_field_instance()
-        self.assertEqual(field.lookup_class, self.lookup_cls)
-
-    def test_init_with_limit(self):
-        field = self.get_field_instance(limit=10)
-        self.assertEqual(field.limit, 10)
-        self.assertEqual(field.widget.limit, 10)
-
-    def test_clean(self):
-        self.fail('This test has not yet been written')
-
-    def test_dotted_path(self):
-        """
-        Ensure lookup_class can be imported from a dotted path.
-        """
-        dotted_path = '.'.join([self.lookup_cls.__module__, self.lookup_cls.__name__])
-        field = self.field_cls(dotted_path)
-        self.assertEqual(field.lookup_class, self.lookup_cls)
-
-    def test_invalid_dotted_path(self):
-        """
-        An invalid lookup_class dotted path should raise an ImportError.
-        """
-        with self.assertRaises(ImportError):
-            self.field_cls('that.is.an.invalid.path')
-
-    def test_dotted_path_wrong_type(self):
-        """
-        lookup_class must be a subclass of LookupBase.
-        """
-        dotted_path = 'selectable.forms.fields.AutoCompleteSelectField'
-        with self.assertRaises(TypeError):
-            self.field_cls(dotted_path)
-
-class AutoCompleteSelectFieldTestCase(BaseSelectableTestCase, FieldTestMixin):
-    field_cls = fields.AutoCompleteSelectField
-    lookup_cls = ThingLookup
-
-    def test_clean(self):
-        thing = self.create_thing()
-        field = self.get_field_instance()
-        value = field.clean([thing.name, thing.id])
-        self.assertEqual(thing, value)
-
-    def test_new_not_allowed(self):
-        field = self.get_field_instance()
-        value = self.get_random_string()
-        self.assertRaises(forms.ValidationError, field.clean, [value, ''])
-
-    def test_new_allowed(self):
-        field = self.get_field_instance(allow_new=True)
-        value = self.get_random_string()
-        value = field.clean([value, ''])
-        self.assertTrue(isinstance(value, ThingLookup.model))
-
-    def test_default_widget(self):
-        field = self.get_field_instance()
-        self.assertTrue(isinstance(field.widget, widgets.AutoCompleteSelectWidget))
-
-    def test_alternate_widget(self):
-        widget_cls = widgets.AutoComboboxWidget
-        field = self.get_field_instance(widget=widget_cls)
-        self.assertTrue(isinstance(field.widget, widget_cls))
-
-    def test_alternate_widget_instance(self):
-        widget = widgets.AutoComboboxWidget(self.lookup_cls)
-        field = self.get_field_instance(widget=widget)
-        self.assertTrue(isinstance(field.widget, widgets.AutoComboboxWidget))
-
-    def test_invalid_pk(self):
-        field = self.get_field_instance()
-        value = self.get_random_string()
-        self.assertRaises(forms.ValidationError, field.clean, [value, 'XXX'])
-
-
-class AutoCompleteSelectMultipleFieldTestCase(BaseSelectableTestCase, FieldTestMixin):
-    field_cls = fields.AutoCompleteSelectMultipleField
-    lookup_cls = ThingLookup
-
-    def get_field_instance(self, limit=None, widget=None):
-        return self.field_cls(self.lookup_cls, limit=limit, widget=widget)
-
-    def test_clean(self):
-        thing = self.create_thing()
-        field = self.get_field_instance()
-        value = field.clean([thing.id])
-        self.assertEqual([thing], value)
-
-    def test_clean_multiple(self):
-        thing = self.create_thing()
-        other_thing = self.create_thing()
-        field = self.get_field_instance()
-        ids = [thing.id, other_thing.id]
-        value = field.clean(ids)
-        self.assertEqual([thing, other_thing], value)
-
-    def test_default_widget(self):
-        field = self.get_field_instance()
-        self.assertTrue(isinstance(field.widget, widgets.AutoCompleteSelectMultipleWidget))
-
-    def test_alternate_widget(self):
-        widget_cls = widgets.AutoComboboxSelectMultipleWidget
-        field = self.get_field_instance(widget=widget_cls)
-        self.assertTrue(isinstance(field.widget, widget_cls))
-
-    def test_alternate_widget_instance(self):
-        widget = widgets.AutoComboboxSelectMultipleWidget(self.lookup_cls)
-        field = self.get_field_instance(widget=widget)
-        self.assertTrue(isinstance(field.widget, widgets.AutoComboboxSelectMultipleWidget))
-
-    def test_invalid_pk(self):
-        field = self.get_field_instance()
-        value = self.get_random_string()
-        self.assertRaises(forms.ValidationError, field.clean, ['XXX', ])
diff --git a/dep/django-selectable/selectable/tests/test_forms.py b/dep/django-selectable/selectable/tests/test_forms.py
deleted file mode 100644 (file)
index ed6bac9..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-from ..forms import BaseLookupForm
-from .base import BaseSelectableTestCase
-
-
-__all__ = (
-    'BaseLookupFormTestCase',
-)
-
-
-class BaseLookupFormTestCase(BaseSelectableTestCase):
-
-    def get_valid_data(self):
-        data = {
-            'term': 'foo',
-            'limit': 10,
-        }
-        return data
-
-    def test_valid_data(self):
-        data = self.get_valid_data()
-        form = BaseLookupForm(data)
-        self.assertTrue(form.is_valid(), "%s" % form.errors)
-
-    def test_invalid_limit(self):
-        """
-        Test giving the form an invalid limit.
-        """
-
-        data = self.get_valid_data()
-        data['limit'] = 'bar'
-        form = BaseLookupForm(data)
-        self.assertFalse(form.is_valid())
-
-    def test_no_limit(self):
-        """
-        If SELECTABLE_MAX_LIMIT is set and limit is not given then
-        the form will return SELECTABLE_MAX_LIMIT.
-        """
-
-        with self.settings(SELECTABLE_MAX_LIMIT=25):
-            data = self.get_valid_data()
-            if 'limit' in data:
-                del data['limit']
-            form = BaseLookupForm(data)
-            self.assertTrue(form.is_valid(), "%s" % form.errors)
-            self.assertEqual(form.cleaned_data['limit'], 25)
-
-    def test_no_max_set(self):
-        """
-        If SELECTABLE_MAX_LIMIT is not set but given then the form
-        will return the given limit.
-        """
-
-        with self.settings(SELECTABLE_MAX_LIMIT=None):
-            data = self.get_valid_data()
-            form = BaseLookupForm(data)
-            self.assertTrue(form.is_valid(), "%s" % form.errors)
-            if 'limit' in data:
-                self.assertTrue(form.cleaned_data['limit'], data['limit'])
-
-    def test_no_max_set_not_given(self):
-        """
-        If SELECTABLE_MAX_LIMIT is not set and not given then the form
-        will return no limit.
-        """
-
-        with self.settings(SELECTABLE_MAX_LIMIT=None):
-            data = self.get_valid_data()
-            if 'limit' in data:
-                del data['limit']
-            form = BaseLookupForm(data)
-            self.assertTrue(form.is_valid(), "%s" % form.errors)
-            self.assertFalse(form.cleaned_data.get('limit'))
-
-    def test_over_limit(self):
-        """
-        If SELECTABLE_MAX_LIMIT is set and limit given is greater then
-        the form will return SELECTABLE_MAX_LIMIT.
-        """
-
-        with self.settings(SELECTABLE_MAX_LIMIT=25):
-            data = self.get_valid_data()
-            data['limit'] = 125
-            form = BaseLookupForm(data)
-            self.assertTrue(form.is_valid(), "%s" % form.errors)
-            self.assertEqual(form.cleaned_data['limit'], 25)
diff --git a/dep/django-selectable/selectable/tests/test_functional.py b/dep/django-selectable/selectable/tests/test_functional.py
deleted file mode 100644 (file)
index 082927f..0000000
+++ /dev/null
@@ -1,575 +0,0 @@
-"""
-Larger functional tests for fields and widgets.
-"""
-from __future__ import unicode_literals
-
-from django import forms
-
-from ..forms import AutoCompleteSelectField, AutoCompleteSelectMultipleField
-from ..forms import AutoCompleteSelectWidget, AutoComboboxSelectWidget
-from . import ManyThing, OtherThing, ThingLookup
-from .base import BaseSelectableTestCase
-
-
-__all__ = (
-    'FuncAutoCompleteSelectTestCase',
-    'FuncSelectModelChoiceTestCase',
-    'FuncComboboxModelChoiceTestCase',
-    'FuncManytoManyMultipleSelectTestCase',
-    'FuncFormTestCase',
-)
-
-
-class OtherThingForm(forms.ModelForm):
-
-    thing = AutoCompleteSelectField(lookup_class=ThingLookup)
-
-    class Meta(object):
-        model = OtherThing
-        fields = ('name', 'thing', )
-
-
-class FuncAutoCompleteSelectTestCase(BaseSelectableTestCase):
-
-    def setUp(self):
-        self.test_thing = self.create_thing()
-
-    def test_valid_form(self):
-        "Valid form using an AutoCompleteSelectField."
-        data = {
-            'name': self.get_random_string(),
-            'thing_0': self.test_thing.name,  # Text input
-            'thing_1': self.test_thing.pk,  # Hidden input
-        }
-        form = OtherThingForm(data=data)
-        self.assertTrue(form.is_valid(), str(form.errors))
-
-    def test_invalid_form_missing_selected_pk(self):
-        "Invalid form using an AutoCompleteSelectField."
-        data = {
-            'name': self.get_random_string(),
-            'thing_0': self.test_thing.name,  # Text input
-            'thing_1': '',  # Hidden input
-        }
-        form = OtherThingForm(data=data)
-        self.assertFalse(form.is_valid(), 'Form should not be valid')
-        self.assertFalse('name' in form.errors)
-        self.assertTrue('thing' in form.errors)
-
-    def test_invalid_form_missing_name(self):
-        "Invalid form using an AutoCompleteSelectField."
-        data = {
-            'name': '',
-            'thing_0': self.test_thing.name,  # Text input
-            'thing_1': self.test_thing.pk,  # Hidden input
-        }
-        form = OtherThingForm(data=data)
-        self.assertFalse(form.is_valid(), 'Form should not be valid')
-        self.assertTrue('name' in form.errors)
-        self.assertFalse('thing' in form.errors)
-
-    def test_invalid_but_still_selected(self):
-        "Invalid form should keep selected item."
-        data = {
-            'name': '',
-            'thing_0': self.test_thing.name,  # Text input
-            'thing_1': self.test_thing.pk,  # Hidden input
-        }
-        form = OtherThingForm(data=data)
-        self.assertFalse(form.is_valid(), 'Form should not be valid')
-        rendered_form = form.as_p()
-        # Selected text should be populated
-        self.assertInHTML(
-            '''
-            <input data-selectable-allow-new="false" data-selectable-type="text"
-                data-selectable-url="/selectable-tests/selectable-thinglookup/"
-                id="id_thing_0" name="thing_0" type="text" value="{}" {} />
-            '''.format(self.test_thing.name,
-                       'required' if hasattr(form, 'use_required_attribute') else ''),
-            rendered_form
-        )
-        # Selected pk should be populated
-        self.assertInHTML(
-            '''
-            <input data-selectable-type="hidden" name="thing_1" id="id_thing_1"
-                type="hidden" value="{}" {} />
-            '''.format(self.test_thing.pk,
-                       'required' if hasattr(form, 'use_required_attribute') else ''),
-            rendered_form,
-        )
-
-    def test_populate_from_model(self):
-        "Populate from existing model."
-        other_thing = OtherThing.objects.create(thing=self.test_thing, name='a')
-        form = OtherThingForm(instance=other_thing)
-        rendered_form = form.as_p()
-        # Selected text should be populated
-        self.assertInHTML(
-            '''
-            <input data-selectable-allow-new="false" data-selectable-type="text"
-                data-selectable-url="/selectable-tests/selectable-thinglookup/"
-                id="id_thing_0" name="thing_0" type="text" value="{}" {} />
-            '''.format(self.test_thing.name,
-                       'required' if hasattr(form, 'use_required_attribute') else ''),
-            rendered_form
-        )
-        # Selected pk should be populated
-        self.assertInHTML(
-            '''
-            <input data-selectable-type="hidden" name="thing_1" id="id_thing_1"
-                type="hidden" value="{}" {} />
-            '''.format(self.test_thing.pk,
-                       'required' if hasattr(form, 'use_required_attribute') else ''),
-            rendered_form
-        )
-
-
-class SelectWidgetForm(forms.ModelForm):
-
-    class Meta(object):
-        model = OtherThing
-        fields = ('name', 'thing', )
-        widgets = {
-            'thing': AutoCompleteSelectWidget(lookup_class=ThingLookup)
-        }
-
-
-class FuncSelectModelChoiceTestCase(BaseSelectableTestCase):
-    """
-    Functional tests for AutoCompleteSelectWidget compatibility
-    with a ModelChoiceField.
-    """
-
-    def setUp(self):
-        self.test_thing = self.create_thing()
-
-    def test_valid_form(self):
-        "Valid form using an AutoCompleteSelectWidget."
-        data = {
-            'name': self.get_random_string(),
-            'thing_0': self.test_thing.name,  # Text input
-            'thing_1': self.test_thing.pk,  # Hidden input
-        }
-        form = SelectWidgetForm(data=data)
-        self.assertTrue(form.is_valid(), str(form.errors))
-
-    def test_missing_pk(self):
-        "Invalid form (missing required pk) using an AutoCompleteSelectWidget."
-        data = {
-            'name': self.get_random_string(),
-            'thing_0': self.test_thing.name,  # Text input
-            'thing_1': '',  # Hidden input missing
-        }
-        form = SelectWidgetForm(data=data)
-        self.assertFalse(form.is_valid())
-        self.assertTrue('thing' in form.errors)
-
-    def test_invalid_pk(self):
-        "Invalid form (invalid pk value) using an AutoCompleteSelectWidget."
-        data = {
-            'name': self.get_random_string(),
-            'thing_0': self.test_thing.name,  # Text input
-            'thing_1': 'XXX',  # Hidden input doesn't match a PK
-        }
-        form = SelectWidgetForm(data=data)
-        self.assertFalse(form.is_valid())
-        self.assertTrue('thing' in form.errors)
-
-    def test_post_compatibility(self):
-        """
-        If new items are not allowed then the original field
-        name can be included in the POST with the selected id.
-        """
-        data = {
-            'name': self.get_random_string(),
-            'thing': self.test_thing.pk,
-        }
-        form = SelectWidgetForm(data=data)
-        self.assertTrue(form.is_valid(), str(form.errors))
-
-
-class ComboboxSelectWidgetForm(forms.ModelForm):
-
-    class Meta(object):
-        model = OtherThing
-        fields = ('name', 'thing', )
-        widgets = {
-            'thing': AutoComboboxSelectWidget(lookup_class=ThingLookup)
-        }
-
-
-class FuncComboboxModelChoiceTestCase(BaseSelectableTestCase):
-    """
-    Functional tests for AutoComboboxSelectWidget compatibility
-    with a ModelChoiceField.
-    """
-
-    def setUp(self):
-        self.test_thing = self.create_thing()
-
-    def test_valid_form(self):
-        "Valid form using an AutoComboboxSelectWidget."
-        data = {
-            'name': self.get_random_string(),
-            'thing_0': self.test_thing.name,  # Text input
-            'thing_1': self.test_thing.pk,  # Hidden input
-        }
-        form = ComboboxSelectWidgetForm(data=data)
-        self.assertTrue(form.is_valid(), str(form.errors))
-
-    def test_missing_pk(self):
-        "Invalid form (missing required pk) using an AutoComboboxSelectWidget."
-        data = {
-            'name': self.get_random_string(),
-            'thing_0': self.test_thing.name,  # Text input
-            'thing_1': '',  # Hidden input missing
-        }
-        form = ComboboxSelectWidgetForm(data=data)
-        self.assertFalse(form.is_valid())
-        self.assertTrue('thing' in form.errors)
-
-    def test_invalid_pk(self):
-        "Invalid form (invalid pk value) using an AutoComboboxSelectWidget."
-        data = {
-            'name': self.get_random_string(),
-            'thing_0': self.test_thing.name,  # Text input
-            'thing_1': 'XXX',  # Hidden input doesn't match a PK
-        }
-        form = ComboboxSelectWidgetForm(data=data)
-        self.assertFalse(form.is_valid())
-        self.assertTrue('thing' in form.errors)
-
-    def test_post_compatibility(self):
-        """
-        If new items are not allowed then the original field
-        name can be included in the POST with the selected id.
-        """
-        data = {
-            'name': self.get_random_string(),
-            'thing': self.test_thing.pk,
-        }
-        form = ComboboxSelectWidgetForm(data=data)
-        self.assertTrue(form.is_valid(), str(form.errors))
-
-
-class ManyThingForm(forms.ModelForm):
-
-    things = AutoCompleteSelectMultipleField(lookup_class=ThingLookup)
-
-    class Meta(object):
-        model = ManyThing
-        fields = ('name', 'things', )
-
-
-class FuncManytoManyMultipleSelectTestCase(BaseSelectableTestCase):
-    """
-    Functional tests for AutoCompleteSelectMultipleField compatibility
-    with a ManyToManyField.
-    """
-
-    def setUp(self):
-        self.test_thing = self.create_thing()
-
-    def test_valid_form(self):
-        "Valid form using an AutoCompleteSelectMultipleField."
-        data = {
-            'name': self.get_random_string(),
-            'things_0': '',  # Text input
-            'things_1': [self.test_thing.pk, ],  # Hidden inputs
-        }
-        form = ManyThingForm(data=data)
-        self.assertTrue(form.is_valid(), str(form.errors))
-
-    def test_valid_save(self):
-        "Saving data from a valid form."
-        data = {
-            'name': self.get_random_string(),
-            'things_0': '',  # Text input
-            'things_1': [self.test_thing.pk, ],  # Hidden inputs
-        }
-        form = ManyThingForm(data=data)
-        manything = form.save()
-        self.assertEqual(manything.name, data['name'])
-        things = manything.things.all()
-        self.assertEqual(things.count(), 1)
-        self.assertTrue(self.test_thing in things)
-
-    def test_not_required(self):
-        "Valid form where many to many is not required."
-        data = {
-            'name': self.get_random_string(),
-            'things_0': '',  # Text input
-            'things_1': [],  # Hidden inputs
-        }
-        form = ManyThingForm(data=data)
-        form.fields['things'].required = False
-        self.assertTrue(form.is_valid(), str(form.errors))
-
-    def test_not_required_save(self):
-        "Saving data when many to many is not required."
-        data = {
-            'name': self.get_random_string(),
-            'things_0': '',  # Text input
-            'things_1': [],  # Hidden inputs
-        }
-        form = ManyThingForm(data=data)
-        form.fields['things'].required = False
-        manything = form.save()
-        self.assertEqual(manything.name, data['name'])
-        things = manything.things.all()
-        self.assertEqual(things.count(), 0)
-
-    def test_has_changed(self):
-        "Populate intial data from a model."
-        manything = ManyThing.objects.create(name='Foo')
-        thing_1 = self.create_thing()
-        manything.things.add(thing_1)
-        data = {
-            'name': manything.name,
-            'things_0': '',  # Text input
-            'things_1': [thing_1.pk],  # Hidden inputs
-        }
-        form = ManyThingForm(data=data, instance=manything)
-        self.assertFalse(form.has_changed(), str(form.changed_data))
-
-    def test_post_compatibility(self):
-        """
-        If new items are not allowed then the original field
-        name can be included in the POST with the selected ids.
-        """
-        data = {
-            'name': self.get_random_string(),
-            'things': [self.test_thing.pk, ],
-        }
-        form = ManyThingForm(data=data)
-        self.assertTrue(form.is_valid(), str(form.errors))
-
-    def test_render_form(self):
-        thing_1 = self.create_thing()
-        manything = ManyThing.objects.create(name='Foo')
-        manything.things.add(thing_1)
-        form = ManyThingForm(instance=manything)
-        rendered = form.as_p()
-        self.assertIn('title="{0}"'.format(thing_1.name),
-                      rendered)
-
-
-class SimpleForm(forms.Form):
-    "Non-model form usage."
-    thing = AutoCompleteSelectField(lookup_class=ThingLookup)
-    new_thing = AutoCompleteSelectField(lookup_class=ThingLookup, allow_new=True)
-    things = AutoCompleteSelectMultipleField(lookup_class=ThingLookup)
-
-
-class FuncFormTestCase(BaseSelectableTestCase):
-    """
-    Functional tests for using AutoCompleteSelectField
-    and AutoCompleteSelectMultipleField outside the context
-    of a ModelForm.
-    """
-
-    def setUp(self):
-        self.test_thing = self.create_thing()
-
-    def test_blank_new_item(self):
-        "Regression test for #91. new_thing is required but both are blank."
-        data = {
-            'thing_0': self.test_thing.name,
-            'thing_1': self.test_thing.pk,
-            'new_thing_0': '',
-            'new_thing_1': '',
-            'things_0': '',
-            'things_1': [self.test_thing.pk, ]
-        }
-        form = SimpleForm(data=data)
-        self.assertFalse(form.is_valid())
-        self.assertTrue('new_thing' in form.errors)
-
-    def test_has_changed_with_empty_permitted(self):
-        """
-        Regression test for #92. has_changed fails when there is no initial and
-        allow_new=False.
-        """
-        data = {
-            'thing_0': '',
-            'thing_1': self.test_thing.pk,
-            'new_thing_0': self.test_thing.name,
-            'new_thing_1': self.test_thing.pk,
-            'things_0': '',
-            'things_1': [self.test_thing.pk, ]
-        }
-        form = SimpleForm(data=data, empty_permitted=True, use_required_attribute=False)
-        self.assertTrue(form.has_changed())
-        self.assertTrue(form.is_valid(), str(form.errors))
-
-    def test_not_changed(self):
-        """
-        Regression test for #92. has_changed fails when there is no initial and
-        allow_new=False.
-        """
-        data = {
-            'thing_0': self.test_thing.name,
-            'thing_1': self.test_thing.pk,
-            'new_thing_0': self.test_thing.name,
-            'new_thing_1': self.test_thing.pk,
-            'things_0': '',
-            'things_1': [self.test_thing.pk, ]
-        }
-        initial = {
-            'thing': self.test_thing.pk,
-            'new_thing': self.test_thing.pk,
-            'things': [self.test_thing.pk, ]
-        }
-        form = SimpleForm(data=data, initial=initial)
-        self.assertFalse(form.has_changed())
-        self.assertTrue(form.is_valid(), str(form.errors))
-
-    def test_not_changed_with_empty_permitted(self):
-        """
-        Regression test for #92. has_changed fails when there is no initial and
-        allow_new=False.
-        """
-        data = {
-            'thing_0': '',
-            'thing_1': '',
-            'new_thing_0': '',
-            'new_thing_1': '',
-            'things_0': '',
-            'things_1': '',
-        }
-        initial = {
-            'thing': '',
-            'new_thing': '',
-            'things': '',
-        }
-        form = SimpleForm(data=data, initial=initial, empty_permitted=True, use_required_attribute=False)
-        self.assertFalse(form.has_changed(), str(form.changed_data))
-        self.assertTrue(form.is_valid(), str(form.errors))
-
-    def test_no_initial_with_empty_permitted(self):
-        """
-        If empty data is submitted and allowed with no initial then
-        the form should not be seen as changed.
-        """
-        data = {
-            'thing_0': '',
-            'thing_1': '',
-            'new_thing_0': '',
-            'new_thing_1': '',
-            'things_0': '',
-            'things_1': '',
-        }
-        form = SimpleForm(data=data, empty_permitted=True, use_required_attribute=False)
-        self.assertFalse(form.has_changed(), str(form.changed_data))
-        self.assertTrue(form.is_valid(), str(form.errors))
-
-    def test_no_data_with_empty_permitted(self):
-        """
-        If no data is submitted and allowed with no initial then
-        the form should not be seen as changed.
-        """
-        form = SimpleForm(data={}, empty_permitted=True, use_required_attribute=False)
-        self.assertFalse(form.has_changed(), str(form.changed_data))
-        self.assertTrue(form.is_valid(), str(form.errors))
-
-    def test_select_multiple_changed(self):
-        """
-        Detect changes for a multiple select input with and without
-        initial data.
-        """
-        data = {
-            'thing_0': '',
-            'thing_1': '',
-            'new_thing_0': '',
-            'new_thing_1': '',
-            'things_0': '',
-            'things_1': [self.test_thing.pk, ]
-        }
-        form = SimpleForm(data=data)
-        self.assertTrue(form.has_changed())
-        self.assertTrue('things' in form.changed_data)
-
-        initial = {
-            'thing': '',
-            'new_thing': '',
-            'things': [self.test_thing.pk, ],
-        }
-        form = SimpleForm(data=data, initial=initial)
-        self.assertFalse(form.has_changed(), str(form.changed_data))
-
-        initial = {
-            'thing': '',
-            'new_thing': '',
-            'things': [],
-        }
-        form = SimpleForm(data=data, initial=initial)
-        self.assertTrue(form.has_changed())
-        self.assertTrue('things' in form.changed_data)
-
-    def test_single_select_changed(self):
-        """
-        Detect changes for a single select input with and without
-        initial data.
-        """
-        data = {
-            'thing_0': '',
-            'thing_1': self.test_thing.pk,
-            'new_thing_0': '',
-            'new_thing_1': '',
-            'things_0': '',
-            'things_1': ''
-        }
-        form = SimpleForm(data=data)
-        self.assertTrue(form.has_changed())
-        self.assertTrue('thing' in form.changed_data)
-
-        initial = {
-            'thing': self.test_thing.pk,
-            'new_thing': '',
-            'things': '',
-        }
-        form = SimpleForm(data=data, initial=initial)
-        self.assertFalse(form.has_changed(), str(form.changed_data))
-
-        initial = {
-            'thing': '',
-            'new_thing': '',
-            'things': '',
-        }
-        form = SimpleForm(data=data, initial=initial)
-        self.assertTrue(form.has_changed())
-        self.assertTrue('thing' in form.changed_data)
-
-    def test_new_select_changed(self):
-        """
-        Detect changes for a single select input which allows new items
-        with and without initial data.
-        """
-        data = {
-            'thing_0': '',
-            'thing_1': '',
-            'new_thing_0': 'Foo',
-            'new_thing_1': '',
-            'things_0': '',
-            'things_1': ''
-        }
-        form = SimpleForm(data=data)
-        self.assertTrue(form.has_changed())
-        self.assertTrue('new_thing' in form.changed_data)
-
-        initial = {
-            'thing': '',
-            'new_thing': ['Foo', None],
-            'things': '',
-        }
-        form = SimpleForm(data=data, initial=initial)
-        self.assertFalse(form.has_changed(), str(form.changed_data))
-
-        initial = {
-            'thing': '',
-            'new_thing': '',
-            'things': '',
-        }
-        form = SimpleForm(data=data, initial=initial)
-        self.assertTrue(form.has_changed())
-        self.assertTrue('new_thing' in form.changed_data)
diff --git a/dep/django-selectable/selectable/tests/test_templatetags.py b/dep/django-selectable/selectable/tests/test_templatetags.py
deleted file mode 100644 (file)
index 74af539..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-from django.template import Template, Context
-
-from .base import BaseSelectableTestCase
-
-__all__ = (
-    'JqueryTagTestCase',
-    'ThemeTagTestCase',
-)
-
-
-class JqueryTagTestCase(BaseSelectableTestCase):
-
-    def assertJQueryVersion(self, result, version):
-        expected = "//ajax.googleapis.com/ajax/libs/jquery/%s/jquery.min.js" % version
-        self.assertTrue(expected in result)
-
-    def assertUIVersion(self, result, version):
-        expected = "//ajax.googleapis.com/ajax/libs/jqueryui/%s/jquery-ui.js" % version
-        self.assertTrue(expected in result)
-
-    def test_render(self):
-        "Render template tag with default versions."
-        template = Template("{% load selectable_tags %}{% include_jquery_libs %}")
-        context = Context({})
-        result = template.render(context)
-        self.assertJQueryVersion(result, '1.12.4')
-        self.assertUIVersion(result, '1.11.4')
-
-    def test_render_jquery_version(self):
-        "Render template tag with specified jQuery version."
-        template = Template("{% load selectable_tags %}{% include_jquery_libs '1.4.3' %}")
-        context = Context({})
-        result = template.render(context)
-        self.assertJQueryVersion(result, '1.4.3')
-
-    def test_render_variable_jquery_version(self):
-        "Render using jQuery version from the template context."
-        version = '1.4.3'
-        template = Template("{% load selectable_tags %}{% include_jquery_libs version %}")
-        context = Context({'version': version})
-        result = template.render(context)
-        self.assertJQueryVersion(result, '1.4.3')
-
-    def test_render_jquery_ui_version(self):
-        "Render template tag with specified jQuery UI version."
-        template = Template("{% load selectable_tags %}{% include_jquery_libs '1.4.3' '1.8.13' %}")
-        context = Context({})
-        result = template.render(context)
-        self.assertUIVersion(result, '1.8.13')
-
-    def test_render_variable_jquery_ui_version(self):
-        "Render using jQuery UI version from the template context."
-        version = '1.8.13'
-        template = Template("{% load selectable_tags %}{% include_jquery_libs '1.4.3' version %}")
-        context = Context({'version': version})
-        result = template.render(context)
-        self.assertUIVersion(result, '1.8.13')
-
-    def test_render_no_jquery(self):
-        "Render template tag without jQuery."
-        template = Template("{% load selectable_tags %}{% include_jquery_libs '' %}")
-        context = Context({})
-        result = template.render(context)
-        self.assertTrue('jquery.min.js' not in result)
-
-    def test_render_no_jquery_ui(self):
-        "Render template tag without jQuery UI."
-        template = Template("{% load selectable_tags %}{% include_jquery_libs '1.7.2' '' %}")
-        context = Context({})
-        result = template.render(context)
-        self.assertTrue('jquery-ui.js' not in result)
-
-
-class ThemeTagTestCase(BaseSelectableTestCase):
-
-    def assertUICSS(self, result, theme, version):
-        expected = "//ajax.googleapis.com/ajax/libs/jqueryui/%s/themes/%s/jquery-ui.css" % (version, theme)
-        self.assertTrue(expected in result)
-
-    def test_render(self):
-        "Render template tag with default settings."
-        template = Template("{% load selectable_tags %}{% include_ui_theme %}")
-        context = Context({})
-        result = template.render(context)
-        self.assertUICSS(result, 'smoothness', '1.11.4')
-
-    def test_render_version(self):
-        "Render template tag with alternate version."
-        template = Template("{% load selectable_tags %}{% include_ui_theme 'base' '1.8.13' %}")
-        context = Context({})
-        result = template.render(context)
-        self.assertUICSS(result, 'base', '1.8.13')
-
-    def test_variable_version(self):
-        "Render using version from content variable."
-        version = '1.8.13'
-        template = Template("{% load selectable_tags %}{% include_ui_theme 'base' version %}")
-        context = Context({'version': version})
-        result = template.render(context)
-        self.assertUICSS(result, 'base', version)
-
-    def test_render_theme(self):
-        "Render template tag with alternate theme."
-        template = Template("{% load selectable_tags %}{% include_ui_theme 'ui-lightness' %}")
-        context = Context({})
-        result = template.render(context)
-        self.assertUICSS(result, 'ui-lightness', '1.11.4')
-
-    def test_variable_theme(self):
-        "Render using theme from content variable."
-        theme = 'ui-lightness'
-        template = Template("{% load selectable_tags %}{% include_ui_theme theme %}")
-        context = Context({'theme': theme})
-        result = template.render(context)
-        self.assertUICSS(result, theme, '1.11.4')
diff --git a/dep/django-selectable/selectable/tests/test_views.py b/dep/django-selectable/selectable/tests/test_views.py
deleted file mode 100644 (file)
index c29ca09..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-from __future__ import division
-
-import json
-
-from django.conf import settings
-from django.urls import reverse
-from django.test import override_settings
-
-from . import ThingLookup
-from .base import BaseSelectableTestCase
-
-
-__all__ = (
-    'SelectableViewTest',
-)
-
-
-@override_settings(SELECTABLE_MAX_LIMIT=25)
-class SelectableViewTest(BaseSelectableTestCase):
-
-    def setUp(self):
-        super(SelectableViewTest, self).setUp()
-        self.url = ThingLookup.url()
-        self.lookup = ThingLookup()
-        self.thing = self.create_thing()
-        self.other_thing = self.create_thing()
-
-    def test_response_type(self):
-        response = self.client.get(self.url)
-        self.assertEqual(response['Content-Type'], 'application/json')
-
-    def test_response_keys(self):
-        response = self.client.get(self.url)
-        data = json.loads(response.content.decode('utf-8'))
-        for result in data.get('data'):
-            self.assertTrue('id' in result)
-            self.assertTrue('value' in result)
-            self.assertTrue('label' in result)
-
-    def test_no_term_lookup(self):
-        data = {}
-        response = self.client.get(self.url, data)
-        data = json.loads(response.content.decode('utf-8'))
-        self.assertEqual(len(data), 2)
-
-    def test_simple_term_lookup(self):
-        data = {'term': self.thing.name}
-        response = self.client.get(self.url, data)
-        data = json.loads(response.content.decode('utf-8'))
-        self.assertEqual(len(data), 2)
-        self.assertEqual(len(data.get('data')), 1)
-
-    def test_unknown_lookup(self):
-        unknown_url = reverse('selectable-lookup', args=["XXXXXXX"])
-        response = self.client.get(unknown_url)
-        self.assertEqual(response.status_code, 404)
-
-    def test_basic_limit(self):
-        for i in range(settings.SELECTABLE_MAX_LIMIT):
-            self.create_thing(data={'name': 'Thing%s' % i})
-        response = self.client.get(self.url)
-        data = json.loads(response.content.decode('utf-8'))
-        self.assertEqual(len(data.get('data')), settings.SELECTABLE_MAX_LIMIT)
-        meta = data.get('meta')
-        self.assertTrue('next_page' in meta)
-
-    def test_get_next_page(self):
-        for i in range(settings.SELECTABLE_MAX_LIMIT * 2):
-            self.create_thing(data={'name': 'Thing%s' % i})
-        data = {'term': 'Thing', 'page': 2}
-        response = self.client.get(self.url, data)
-        data = json.loads(response.content.decode('utf-8'))
-        self.assertEqual(len(data.get('data')), settings.SELECTABLE_MAX_LIMIT)
-        # No next page
-        meta = data.get('meta')
-        self.assertFalse('next_page' in meta)
-
-    def test_request_more_than_max(self):
-        for i in range(settings.SELECTABLE_MAX_LIMIT):
-            self.create_thing(data={'name': 'Thing%s' % i})
-        data = {'term': '', 'limit': settings.SELECTABLE_MAX_LIMIT * 2}
-        response = self.client.get(self.url)
-        data = json.loads(response.content.decode('utf-8'))
-        self.assertEqual(len(data.get('data')), settings.SELECTABLE_MAX_LIMIT)
-
-    def test_request_less_than_max(self):
-        for i in range(settings.SELECTABLE_MAX_LIMIT):
-            self.create_thing(data={'name': 'Thing%s' % i})
-        new_limit = settings.SELECTABLE_MAX_LIMIT // 2
-        data = {'term': '', 'limit': new_limit}
-        response = self.client.get(self.url, data)
-        data = json.loads(response.content.decode('utf-8'))
-        self.assertEqual(len(data.get('data')), new_limit)
diff --git a/dep/django-selectable/selectable/tests/test_widgets.py b/dep/django-selectable/selectable/tests/test_widgets.py
deleted file mode 100644 (file)
index 5209717..0000000
+++ /dev/null
@@ -1,463 +0,0 @@
-import json
-
-from django import forms
-from django.utils.http import urlencode
-
-from . import Thing, ThingLookup
-from ..compat import urlparse
-from ..forms import widgets
-from .base import BaseSelectableTestCase, parsed_inputs, parsed_widget_attributes
-
-__all__ = (
-    'AutoCompleteWidgetTestCase',
-    'AutoCompleteSelectWidgetTestCase',
-    'AutoComboboxWidgetTestCase',
-    'AutoComboboxSelectWidgetTestCase',
-    'AutoCompleteSelectMultipleWidgetTestCase',
-    'AutoComboboxSelectMultipleWidgetTestCase',
-)
-
-
-class WidgetTestMixin(object):
-    widget_cls = None
-    lookup_cls = None
-
-    def get_widget_instance(self, **kwargs):
-        return self.__class__.widget_cls(self.__class__.lookup_cls, **kwargs)
-
-    def test_init(self):
-        widget = self.get_widget_instance()
-        self.assertEqual(widget.lookup_class, self.__class__.lookup_cls)
-
-    def test_dotted_path(self):
-        """
-        Ensure lookup_class can be imported from a dotted path.
-        """
-        dotted_path = '.'.join([self.__class__.lookup_cls.__module__, self.__class__.lookup_cls.__name__])
-        widget = self.__class__.widget_cls(dotted_path)
-        self.assertEqual(widget.lookup_class, self.__class__.lookup_cls)
-
-    def test_invalid_dotted_path(self):
-        """
-        An invalid lookup_class dotted path should raise an ImportError.
-        """
-        with self.assertRaises(ImportError):
-            self.__class__.widget_cls('that.is.an.invalid.path')
-
-    def test_dotted_path_wrong_type(self):
-        """
-        lookup_class must be a subclass of LookupBase.
-        """
-        dotted_path = 'selectable.forms.widgets.AutoCompleteWidget'
-        with self.assertRaises(TypeError):
-            self.__class__.widget_cls(dotted_path)
-
-
-class AutoCompleteWidgetTestCase(BaseSelectableTestCase, WidgetTestMixin):
-    widget_cls = widgets.AutoCompleteWidget
-    lookup_cls = ThingLookup
-
-    def test_rendered_attrs(self):
-        widget = self.get_widget_instance()
-        attrs = parsed_widget_attributes(widget)
-        self.assertTrue('data-selectable-url' in attrs)
-        self.assertTrue('data-selectable-type' in attrs)
-        self.assertTrue('data-selectable-allow-new' in attrs)
-
-    def test_update_query_parameters(self):
-        params = {'active': 1}
-        widget = self.get_widget_instance()
-        widget.update_query_parameters(params)
-        attrs = parsed_widget_attributes(widget)
-        url = attrs['data-selectable-url']
-        parse = urlparse(url)
-        query = parse.query
-        self.assertEqual(query, urlencode(params))
-
-    def test_limit_parameter(self):
-        widget = self.get_widget_instance(limit=10)
-        attrs = parsed_widget_attributes(widget)
-        url = attrs['data-selectable-url']
-        parse = urlparse(url)
-        query = parse.query
-        self.assertTrue('limit=10' in query)
-
-    def test_initial_query_parameters(self):
-        params = {'active': 1}
-        widget = self.get_widget_instance(query_params=params)
-        attrs = parsed_widget_attributes(widget)
-        url = attrs['data-selectable-url']
-        parse = urlparse(url)
-        query = parse.query
-        self.assertEqual(query, urlencode(params))
-
-    def test_build_selectable_options(self):
-        "Serialize selectable options as json in data attribute."
-        options = {'autoFocus': True}
-        widget = self.get_widget_instance(attrs={'data-selectable-options': options})
-        attrs = parsed_widget_attributes(widget)
-        self.assertTrue('data-selectable-options' in attrs)
-        self.assertEqual(attrs['data-selectable-options'], json.dumps(options))
-
-
-class AutoCompleteSelectWidgetTestCase(BaseSelectableTestCase, WidgetTestMixin):
-    widget_cls = widgets.AutoCompleteSelectWidget
-    lookup_cls = ThingLookup
-
-    def test_has_complete_widget(self):
-        widget = self.get_widget_instance()
-        self.assertEqual(widget.widgets[0].__class__, widgets.AutoCompleteWidget)
-
-    def test_has_hidden_widget(self):
-        widget = self.get_widget_instance()
-        self.assertEqual(widget.widgets[1].__class__, forms.HiddenInput)
-
-    def test_hidden_type(self):
-        widget = self.get_widget_instance()
-        attrs = parsed_widget_attributes(widget.widgets[1])
-        self.assertTrue('data-selectable-type' in attrs)
-        self.assertEqual(attrs['data-selectable-type'], 'hidden')
-
-    def test_update_query_parameters(self):
-        params = {'active': 1}
-        widget = self.get_widget_instance()
-        widget.update_query_parameters(params)
-        attrs = parsed_widget_attributes(widget.widgets[0])
-        url = attrs['data-selectable-url']
-        parse = urlparse(url)
-        query = parse.query
-        self.assertEqual(query, urlencode(params))
-
-    def test_limit_parameter(self):
-        widget = self.get_widget_instance(limit=10)
-        attrs = parsed_widget_attributes(widget.widgets[0])
-        url = attrs['data-selectable-url']
-        parse = urlparse(url)
-        query = parse.query
-        self.assertTrue('limit=10' in query)
-
-    def test_initial_query_parameters(self):
-        params = {'active': 1}
-        widget = self.get_widget_instance(query_params=params)
-        attrs = parsed_widget_attributes(widget.widgets[0])
-        url = attrs['data-selectable-url']
-        parse = urlparse(url)
-        query = parse.query
-        self.assertEqual(query, urlencode(params))
-
-    def test_build_selectable_options(self):
-        "Serialize selectable options as json in data attribute."
-        options = {'autoFocus': True}
-        widget = self.get_widget_instance(attrs={'data-selectable-options': options})
-        attrs = parsed_widget_attributes(widget.widgets[0])
-        self.assertTrue('data-selectable-options' in attrs)
-        self.assertEqual(attrs['data-selectable-options'], json.dumps(options))
-
-    def test_postdata_compatible_with_select(self):
-        "Checks postdata for values that a select widget would generate."
-        postdata = {'fruit': '1'}
-        widget = self.get_widget_instance()
-        widget_val = widget.value_from_datadict(postdata, [], 'fruit')
-        self.assertEqual(widget_val, '1')
-
-
-class AutoComboboxWidgetTestCase(BaseSelectableTestCase, WidgetTestMixin):
-    widget_cls = widgets.AutoComboboxWidget
-    lookup_cls = ThingLookup
-
-    def test_rendered_attrs(self):
-        widget = self.get_widget_instance()
-        attrs = parsed_widget_attributes(widget)
-        self.assertTrue('data-selectable-url' in attrs)
-        self.assertTrue('data-selectable-type' in attrs)
-        self.assertTrue('data-selectable-allow-new' in attrs)
-
-    def test_update_query_parameters(self):
-        params = {'active': 1}
-        widget = self.get_widget_instance()
-        widget.update_query_parameters(params)
-        attrs = parsed_widget_attributes(widget)
-        url = attrs['data-selectable-url']
-        parse = urlparse(url)
-        query = parse.query
-        self.assertEqual(query, urlencode(params))
-
-    def test_limit_parameter(self):
-        widget = self.get_widget_instance(limit=10)
-        attrs = parsed_widget_attributes(widget)
-        url = attrs['data-selectable-url']
-        parse = urlparse(url)
-        query = parse.query
-        self.assertTrue('limit=10' in query)
-
-    def test_initial_query_parameters(self):
-        params = {'active': 1}
-        widget = self.get_widget_instance(query_params=params)
-        attrs = parsed_widget_attributes(widget)
-        url = attrs['data-selectable-url']
-        parse = urlparse(url)
-        query = parse.query
-        self.assertEqual(query, urlencode(params))
-
-    def test_build_selectable_options(self):
-        "Serialize selectable options as json in data attribute."
-        options = {'autoFocus': True}
-        widget = self.get_widget_instance(attrs={'data-selectable-options': options})
-        attrs = parsed_widget_attributes(widget)
-        self.assertTrue('data-selectable-options' in attrs)
-        self.assertEqual(attrs['data-selectable-options'], json.dumps(options))
-
-
-class AutoComboboxSelectWidgetTestCase(BaseSelectableTestCase, WidgetTestMixin):
-    widget_cls = widgets.AutoComboboxSelectWidget
-    lookup_cls = ThingLookup
-
-    def test_has_complete_widget(self):
-        widget = self.get_widget_instance()
-        self.assertEqual(widget.widgets[0].__class__, widgets.AutoComboboxWidget)
-
-    def test_has_hidden_widget(self):
-        widget = self.get_widget_instance()
-        self.assertEqual(widget.widgets[1].__class__, forms.HiddenInput)
-
-    def test_hidden_type(self):
-        widget = self.get_widget_instance()
-        attrs = parsed_widget_attributes(widget.widgets[1])
-        self.assertTrue('data-selectable-type' in attrs)
-        self.assertEqual(attrs['data-selectable-type'], 'hidden')
-
-    def test_update_query_parameters(self):
-        params = {'active': 1}
-        widget = self.get_widget_instance()
-        widget.update_query_parameters(params)
-        attrs = parsed_widget_attributes(widget.widgets[0])
-        url = attrs['data-selectable-url']
-        parse = urlparse(url)
-        query = parse.query
-        self.assertEqual(query, urlencode(params))
-
-    def test_limit_parameter(self):
-        widget = self.get_widget_instance(limit=10)
-        attrs = parsed_widget_attributes(widget.widgets[0])
-        url = attrs['data-selectable-url']
-        parse = urlparse(url)
-        query = parse.query
-        self.assertTrue('limit=10' in query)
-
-    def test_initial_query_parameters(self):
-        params = {'active': 1}
-        widget = self.get_widget_instance(query_params=params)
-        attrs = parsed_widget_attributes(widget.widgets[0])
-        url = attrs['data-selectable-url']
-        parse = urlparse(url)
-        query = parse.query
-        self.assertEqual(query, urlencode(params))
-
-    def test_build_selectable_options(self):
-        "Serialize selectable options as json in data attribute."
-        options = {'autoFocus': True}
-        widget = self.get_widget_instance(attrs={'data-selectable-options': options})
-        attrs = parsed_widget_attributes(widget.widgets[0])
-        self.assertTrue('data-selectable-options' in attrs)
-        self.assertEqual(attrs['data-selectable-options'], json.dumps(options))
-
-
-class AutoCompleteSelectMultipleWidgetTestCase(BaseSelectableTestCase, WidgetTestMixin):
-    widget_cls = widgets.AutoCompleteSelectMultipleWidget
-    lookup_cls = ThingLookup
-
-    def test_has_complete_widget(self):
-        widget = self.get_widget_instance()
-        self.assertEqual(widget.widgets[0].__class__, widgets.AutoCompleteWidget)
-
-    def test_multiple_attr(self):
-        widget = self.get_widget_instance()
-        attrs = parsed_widget_attributes(widget.widgets[0])
-        self.assertTrue('data-selectable-multiple' in attrs)
-        self.assertEqual(attrs['data-selectable-multiple'], 'true')
-
-    def test_has_hidden_widget(self):
-        widget = self.get_widget_instance()
-        self.assertEqual(widget.widgets[1].__class__, widgets.LookupMultipleHiddenInput)
-
-    def test_hidden_type(self):
-        widget = self.get_widget_instance()
-        attrs = parsed_widget_attributes(widget.widgets[1])
-        self.assertTrue('data-selectable-type' in attrs)
-        self.assertEqual(attrs['data-selectable-type'], 'hidden-multiple')
-
-    def test_render_single(self):
-        widget = self.get_widget_instance()
-        val = 4
-        rendered_value = widget.render('field_name', val)
-        inputs = parsed_inputs(rendered_value)
-        field = inputs['field_name_1'][0]
-        attributes = dict(field.attributes)
-        self.assertEqual(attributes['data-selectable-type'], 'hidden-multiple')
-        self.assertEqual(attributes['type'], 'hidden')
-        self.assertEqual(int(attributes['value']), val)
-
-    def test_render_list(self):
-        widget = self.get_widget_instance()
-        list_val = [8, 5]
-        rendered_value = widget.render('field_name', list_val)
-        inputs = parsed_inputs(rendered_value)
-        found_values = []
-        for field in inputs['field_name_1']:
-            attributes = dict(field.attributes)
-            self.assertEqual(attributes['data-selectable-type'], 'hidden-multiple')
-            self.assertEqual(attributes['type'], 'hidden')
-            found_values.append(int(attributes['value']))
-        self.assertListEqual(found_values, list_val)
-
-    def test_render_qs(self):
-        widget = self.get_widget_instance()
-        t1 = self.create_thing()
-        t2 = self.create_thing()
-        qs_val = Thing.objects.filter(pk__in=[t1.pk, t2.pk])
-        rendered_value = widget.render('field_name', qs_val)
-        inputs = parsed_inputs(rendered_value)
-        found_values = []
-        found_titles = []
-        for field in inputs['field_name_1']:
-            attributes = dict(field.attributes)
-            self.assertEqual(attributes['data-selectable-type'], 'hidden-multiple')
-            self.assertEqual(attributes['type'], 'hidden')
-            found_titles.append(attributes['title'])
-            found_values.append(attributes['value'])
-        self.assertListEqual(found_values, [str(t1.pk), str(t2.pk)])
-        self.assertListEqual(found_titles, [t1.name, t2.name])
-
-    def test_update_query_parameters(self):
-        params = {'active': 1}
-        widget = self.get_widget_instance()
-        widget.update_query_parameters(params)
-        attrs = parsed_widget_attributes(widget.widgets[0])
-        url = attrs['data-selectable-url']
-        parse = urlparse(url)
-        query = parse.query
-        self.assertEqual(query, urlencode(params))
-
-    def test_limit_parameter(self):
-        widget = self.get_widget_instance(limit=10)
-        attrs = parsed_widget_attributes(widget.widgets[0])
-        url = attrs['data-selectable-url']
-        parse = urlparse(url)
-        query = parse.query
-        self.assertTrue('limit=10' in query)
-
-    def test_initial_query_parameters(self):
-        params = {'active': 1}
-        widget = self.get_widget_instance(query_params=params)
-        attrs = parsed_widget_attributes(widget.widgets[0])
-        url = attrs['data-selectable-url']
-        parse = urlparse(url)
-        query = parse.query
-        self.assertEqual(query, urlencode(params))
-
-    def test_build_selectable_options(self):
-        "Serialize selectable options as json in data attribute."
-        options = {'autoFocus': True}
-        widget = self.get_widget_instance(attrs={'data-selectable-options': options})
-        attrs = parsed_widget_attributes(widget.widgets[0])
-        self.assertTrue('data-selectable-options' in attrs)
-        self.assertEqual(attrs['data-selectable-options'], json.dumps(options))
-
-
-class AutoComboboxSelectMultipleWidgetTestCase(BaseSelectableTestCase, WidgetTestMixin):
-    widget_cls = widgets.AutoComboboxSelectMultipleWidget
-    lookup_cls = ThingLookup
-
-    def test_has_complete_widget(self):
-        widget = self.get_widget_instance()
-        self.assertEqual(widget.widgets[0].__class__, widgets.AutoComboboxWidget)
-
-    def test_multiple_attr(self):
-        widget = self.get_widget_instance()
-        attrs = parsed_widget_attributes(widget.widgets[0])
-        self.assertTrue('data-selectable-multiple' in attrs)
-        self.assertEqual(attrs['data-selectable-multiple'], 'true')
-
-    def test_has_hidden_widget(self):
-        widget = self.get_widget_instance()
-        self.assertEqual(widget.widgets[1].__class__, widgets.LookupMultipleHiddenInput)
-
-    def test_hidden_type(self):
-        widget = self.get_widget_instance()
-        attrs = parsed_widget_attributes(widget.widgets[1])
-        self.assertTrue('data-selectable-type' in attrs)
-        self.assertEqual(attrs['data-selectable-type'], 'hidden-multiple')
-
-    def test_render_single(self):
-        widget = self.get_widget_instance()
-        val = 4
-        rendered_value = widget.render('field_name', val)
-        inputs = parsed_inputs(rendered_value)
-        field = inputs['field_name_1'][0]
-        attributes = dict(field.attributes)
-        self.assertEqual(attributes['data-selectable-type'], 'hidden-multiple')
-        self.assertEqual(attributes['type'], 'hidden')
-        self.assertEqual(attributes['value'], str(val))
-
-    def test_render_list(self):
-        widget = self.get_widget_instance()
-        list_val = [8, 5]
-        rendered_value = widget.render('field_name', list_val)
-        inputs = parsed_inputs(rendered_value)
-        found_values = []
-        for field in inputs['field_name_1']:
-            attributes = dict(field.attributes)
-            self.assertEqual(attributes['data-selectable-type'], 'hidden-multiple')
-            self.assertEqual(attributes['type'], 'hidden')
-            found_values.append(int(attributes['value']))
-        self.assertListEqual(found_values, list_val)
-
-    def test_render_qs(self):
-        widget = self.get_widget_instance()
-        t1 = self.create_thing()
-        t2 = self.create_thing()
-        qs_val = Thing.objects.filter(pk__in=[t1.pk, t2.pk])
-        rendered_value = widget.render('field_name', qs_val)
-        inputs = parsed_inputs(rendered_value)
-        found_values = []
-        for field in inputs['field_name_1']:
-            attributes = dict(field.attributes)
-            self.assertEqual(attributes['data-selectable-type'], 'hidden-multiple')
-            self.assertEqual(attributes['type'], 'hidden')
-            found_values.append(int(attributes['value']))
-        self.assertListEqual(found_values, [t1.pk, t2.pk])
-
-    def test_update_query_parameters(self):
-        params = {'active': 1}
-        widget = self.get_widget_instance()
-        widget.update_query_parameters(params)
-        attrs = parsed_widget_attributes(widget.widgets[0])
-        url = attrs['data-selectable-url']
-        parse = urlparse(url)
-        query = parse.query
-        self.assertEqual(query, urlencode(params))
-
-    def test_limit_parameter(self):
-        widget = self.get_widget_instance(limit=10)
-        attrs = parsed_widget_attributes(widget.widgets[0])
-        url = attrs['data-selectable-url']
-        parse = urlparse(url)
-        query = parse.query
-        self.assertTrue('limit=10' in query)
-
-    def test_initial_query_parameters(self):
-        params = {'active': 1}
-        widget = self.get_widget_instance(query_params=params)
-        attrs = parsed_widget_attributes(widget.widgets[0])
-        url = attrs['data-selectable-url']
-        parse = urlparse(url)
-        query = parse.query
-        self.assertEqual(query, urlencode(params))
-
-    def test_build_selectable_options(self):
-        "Serialize selectable options as json in data attribute."
-        options = {'autoFocus': True}
-        widget = self.get_widget_instance(attrs={'data-selectable-options': options})
-        attrs = parsed_widget_attributes(widget.widgets[0])
-        self.assertTrue('data-selectable-options' in attrs)
-        self.assertEqual(attrs['data-selectable-options'], json.dumps(options))
diff --git a/dep/django-selectable/selectable/tests/urls.py b/dep/django-selectable/selectable/tests/urls.py
deleted file mode 100644 (file)
index ed7cf46..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-from django.conf.urls import handler404, handler500, include, url
-
-
-handler404 = 'selectable.tests.views.test_404'
-handler500 = 'selectable.tests.views.test_500'
-
-urlpatterns = [
-    url(r'^selectable-tests/', include('selectable.urls')),
-]
diff --git a/dep/django-selectable/selectable/tests/views.py b/dep/django-selectable/selectable/tests/views.py
deleted file mode 100644 (file)
index 83a71d1..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-from django.http import HttpResponseNotFound, HttpResponseServerError
-
-
-def test_404(request, *args, **kwargs):
-    return HttpResponseNotFound()
-
-
-def test_500(request, *args, **kwargs):
-    return HttpResponseServerError()
diff --git a/dep/django-selectable/selectable/urls.py b/dep/django-selectable/selectable/urls.py
deleted file mode 100644 (file)
index eee1b68..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-from django.conf.urls import url
-
-from . import views
-
-
-urlpatterns = [
-    url(r'^(?P<lookup_name>[-\w]+)/$', views.get_lookup, name="selectable-lookup"),
-]
diff --git a/dep/django-selectable/selectable/views.py b/dep/django-selectable/selectable/views.py
deleted file mode 100644 (file)
index 5d8ef62..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-from __future__ import unicode_literals
-
-from django.http import Http404
-
-from selectable.registry import registry
-
-
-def get_lookup(request, lookup_name):
-
-    lookup_cls = registry.get(lookup_name)
-    if lookup_cls is None:
-        raise Http404('Lookup %s not found' % lookup_name)
-
-    lookup = lookup_cls()
-    return lookup.results(request)
-
diff --git a/dep/django-selectable/setup.cfg b/dep/django-selectable/setup.cfg
deleted file mode 100644 (file)
index d4d5fd8..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-[coverage:run]
-branch = true
-omit = */tests/*, example/*, .tox/*, setup.py, runtests.py
-source = .
-
-[coverage:report]
-show_missing = true
-
-
-[bdist_wheel]
-universal = 1
diff --git a/dep/django-selectable/setup.py b/dep/django-selectable/setup.py
deleted file mode 100755 (executable)
index 40b9ab0..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-#!/usr/bin/env python
-import os
-from setuptools import setup, find_packages
-
-
-def read_file(filename):
-    """Read a file into a string"""
-    path = os.path.abspath(os.path.dirname(__file__))
-    filepath = os.path.join(path, filename)
-    try:
-        return open(filepath).read()
-    except IOError:
-        return ''
-
-
-setup(
-    name='django-selectable',
-    version=__import__('selectable').__version__,
-    author='Mark Lavin',
-    author_email='[email protected]',
-    packages=find_packages(exclude=['example']),
-    include_package_data=True,
-    url='https://github.com/mlavin/django-selectable',
-    license='BSD',
-    description=' '.join(__import__('selectable').__doc__.splitlines()).strip(),
-    classifiers=[
-        'Development Status :: 5 - Production/Stable',
-        'Intended Audience :: Developers',
-        'Framework :: Django',
-        'Framework :: Django :: 1.11',
-        'Framework :: Django :: 2.0',
-        'Framework :: Django :: 2.1',
-        'License :: OSI Approved :: BSD License',
-        'Operating System :: OS Independent',
-        'Programming Language :: Python',
-        'Programming Language :: Python :: 2.7',
-        'Programming Language :: Python :: 3',
-        'Programming Language :: Python :: 3.4',
-        'Programming Language :: Python :: 3.5',
-        'Programming Language :: Python :: 3.6',
-        'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
-    ],
-    long_description=read_file('README.rst'),
-    test_suite="runtests.runtests",
-    tests_require=['mock'],
-    zip_safe=False,  # because we're including media that Django needs
-)
diff --git a/dep/django-selectable/tox.ini b/dep/django-selectable/tox.ini
deleted file mode 100644 (file)
index c6c4e96..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-[tox]
-envlist = py{27,34,35,36}-django{111},py{34,35,36}-django{20,21},py35-django_master,docs
-
-[testenv]
-basepython =
-    py27: python2.7
-    py34: python3.4
-    py35: python3.5
-    py36: python3.6
-deps =
-    coverage>=4.0,<4.1
-    django111: Django>=1.11,<2.0
-    django20: Django>=2.0,<2.1
-    django21: Django>=2.1,<2.2
-    django_master: https://github.com/django/django/archive/master.tar.gz
-    py27: mock
-commands = coverage run runtests.py
-
-[testenv:docs]
-basepython = python3.5
-deps =
-    Sphinx
-    Django
-commands =
-    {envbindir}/sphinx-build -a -n -b html -d docs/_build/doctrees docs docs/_build/html
index 4d0430f9581e37d937c096df8924e7eddf8995f9..1c812f4c2c88dbc63f388229d8e910c4cc896fde 100644 (file)
@@ -5,10 +5,7 @@ from django.db.models import Q
 from django.contrib.auth.models import User
 from django.http import Http404
 
-from selectable.forms.widgets import AutoCompleteSelectMultipleWidget
-
 from .models import Patch, MailThread, PatchOnCommitFest, TargetVersion
-from .lookups import UserLookup
 from .widgets import ThreadPickWidget
 from .ajax import _archivesAPI
 
@@ -40,13 +37,14 @@ class CommitFestFilterForm(forms.Form):
 
 
 class PatchForm(forms.ModelForm):
+    selectize_multiple_fields = {
+        'authors': '/lookups/user',
+        'reviewers': '/lookups/user',
+    }
+
     class Meta:
         model = Patch
         exclude = ('commitfests', 'mailthreads', 'modified', 'lastmail', 'subscribers', )
-        widgets = {
-            'authors': AutoCompleteSelectMultipleWidget(lookup_class=UserLookup, position='top'),
-            'reviewers': AutoCompleteSelectMultipleWidget(lookup_class=UserLookup, position='top'),
-        }
 
     def __init__(self, *args, **kwargs):
         super(PatchForm, self).__init__(*args, **kwargs)
@@ -54,6 +52,24 @@ class PatchForm(forms.ModelForm):
         self.fields['reviewers'].help_text = 'Enter part of name to see list'
         self.fields['committer'].label_from_instance = lambda x: '%s %s (%s)' % (x.user.first_name, x.user.last_name, x.user.username)
 
+        # Selectize multiple fields -- don't pre-populate everything
+        for field, url in list(self.selectize_multiple_fields.items()):
+            # If this is a postback of a selectize field, it may contain ids that are not currently
+            # stored in the field. They must still be among the *allowed* values of course, which
+            # are handled by the existing queryset on the field.
+            if self.instance.pk:
+                # If this object isn't created yet, then it by definition has no related
+                # objects, so just bypass the collection of values since it will cause
+                # errors.
+                vals = [o.pk for o in getattr(self.instance, field).all()]
+            else:
+                vals = []
+            if 'data' in kwargs and str(field) in kwargs['data']:
+                vals.extend([x for x in kwargs['data'].getlist(field)])
+            self.fields[field].widget.attrs['data-selecturl'] = url
+            self.fields[field].queryset = self.fields[field].queryset.filter(pk__in=set(vals))
+            self.fields[field].label_from_instance = lambda u: '{} ({})'.format(u.username, u.get_full_name())
+
 
 class NewPatchForm(forms.ModelForm):
     threadmsgid = forms.CharField(max_length=200, required=True, label='Specify thread msgid', widget=ThreadPickWidget)
index 33e2b4e59d4a9b4e124732069897e466e797edef..18d2ef534522555c7bf247eebc8507a2e54eff08 100644 (file)
@@ -1,26 +1,22 @@
+from django.http import HttpResponse, Http404
+from django.db.models import Q
+from django.contrib.auth.decorators import login_required
 from django.contrib.auth.models import User
-from selectable.base import ModelLookup
-from selectable.registry import registry
-from selectable.decorators import login_required
 
+import json
 
-@login_required
-class UserLookup(ModelLookup):
-    model = User
-    search_fields = (
-        'username__icontains',
-        'first_name__icontains',
-        'last_name__icontains',
-    )
-    filters = {'is_active': True, }
-
-    def get_item_value(self, item):
-        # Display for currently selected item
-        return "%s (%s)" % (item.username, item.get_full_name())
 
-    def get_item_label(self, item):
-        # Display for choice listings
-        return "%s (%s)" % (item.username, item.get_full_name())
+@login_required
+def userlookup(request):
+    query = request.GET.get('query', None)
+    if not query:
+        return Http404()
 
+    users = User.objects.filter(
+        Q(is_active=True),
+        Q(username__icontains=query) | Q(first_name__icontains=query) | Q(last_name__icontains=query),
+    )
 
-registry.register(UserLookup)
+    return HttpResponse(json.dumps({
+        'values': [{'id': u.id, 'value': '{} ({})'.format(u.username, u.get_full_name())} for u in users],
+    }), content_type='application/json')
index e3058a32b5c7d413ddd6ce878245d6e84c5deded..0ed83555212bc3fdd597824e93aa2bedbeede044 100644 (file)
@@ -32,21 +32,13 @@ div.form-group div.controls input[type='checkbox'] {
    width: 10px;
 }
 
-div.controls ul.selectable-deck li.selectable-deck-item {
-   display: block;
-}
-
-div.controls ul.selectable-deck li.selectable-deck-item a.selectable-deck-remove {
-   float: none;
-   margin-left: 10px;
-}
-
 div.form-group div.controls input.threadpick-input {
    width: 80%;
    display: inline;
 }
 
 
+
 /*
  * Attach thread dialog
  */
diff --git a/pgcommitfest/commitfest/static/commitfest/css/selectize.css b/pgcommitfest/commitfest/static/commitfest/css/selectize.css
new file mode 100644 (file)
index 0000000..a763839
--- /dev/null
@@ -0,0 +1,323 @@
+/**
+ * selectize.css (v0.12.2)
+ * Copyright (c) 2013–2015 Brian Reavis & contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
+ * file except in compliance with the License. You may obtain a copy of the License at:
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
+ * ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ *
+ * @author Brian Reavis <[email protected]>
+ */
+
+.selectize-control.plugin-drag_drop.multi > .selectize-input > div.ui-sortable-placeholder {
+  visibility: visible !important;
+  background: #f2f2f2 !important;
+  background: rgba(0, 0, 0, 0.06) !important;
+  border: 0 none !important;
+  -webkit-box-shadow: inset 0 0 12px 4px #ffffff;
+  box-shadow: inset 0 0 12px 4px #ffffff;
+}
+.selectize-control.plugin-drag_drop .ui-sortable-placeholder::after {
+  content: '!';
+  visibility: hidden;
+}
+.selectize-control.plugin-drag_drop .ui-sortable-helper {
+  -webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
+  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
+}
+.selectize-dropdown-header {
+  position: relative;
+  padding: 5px 8px;
+  border-bottom: 1px solid #d0d0d0;
+  background: #f8f8f8;
+  -webkit-border-radius: 3px 3px 0 0;
+  -moz-border-radius: 3px 3px 0 0;
+  border-radius: 3px 3px 0 0;
+}
+.selectize-dropdown-header-close {
+  position: absolute;
+  right: 8px;
+  top: 50%;
+  color: #303030;
+  opacity: 0.4;
+  margin-top: -12px;
+  line-height: 20px;
+  font-size: 20px !important;
+}
+.selectize-dropdown-header-close:hover {
+  color: #000000;
+}
+.selectize-dropdown.plugin-optgroup_columns .optgroup {
+  border-right: 1px solid #f2f2f2;
+  border-top: 0 none;
+  float: left;
+  -webkit-box-sizing: border-box;
+  -moz-box-sizing: border-box;
+  box-sizing: border-box;
+}
+.selectize-dropdown.plugin-optgroup_columns .optgroup:last-child {
+  border-right: 0 none;
+}
+.selectize-dropdown.plugin-optgroup_columns .optgroup:before {
+  display: none;
+}
+.selectize-dropdown.plugin-optgroup_columns .optgroup-header {
+  border-top: 0 none;
+}
+.selectize-control.plugin-remove_button [data-value] {
+  position: relative;
+  padding-right: 24px !important;
+}
+.selectize-control.plugin-remove_button [data-value] .remove {
+  z-index: 1;
+  /* fixes ie bug (see #392) */
+  position: absolute;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  width: 17px;
+  text-align: center;
+  font-weight: bold;
+  font-size: 12px;
+  color: inherit;
+  text-decoration: none;
+  vertical-align: middle;
+  display: inline-block;
+  padding: 2px 0 0 0;
+  border-left: 1px solid #d0d0d0;
+  -webkit-border-radius: 0 2px 2px 0;
+  -moz-border-radius: 0 2px 2px 0;
+  border-radius: 0 2px 2px 0;
+  -webkit-box-sizing: border-box;
+  -moz-box-sizing: border-box;
+  box-sizing: border-box;
+}
+.selectize-control.plugin-remove_button [data-value] .remove:hover {
+  background: rgba(0, 0, 0, 0.05);
+}
+.selectize-control.plugin-remove_button [data-value].active .remove {
+  border-left-color: #cacaca;
+}
+.selectize-control.plugin-remove_button .disabled [data-value] .remove:hover {
+  background: none;
+}
+.selectize-control.plugin-remove_button .disabled [data-value] .remove {
+  border-left-color: #ffffff;
+}
+.selectize-control.plugin-remove_button .remove-single {
+  position: absolute;
+  right: 28px;
+  top: 6px;
+  font-size: 23px;
+}
+.selectize-control {
+  position: relative;
+}
+.selectize-dropdown,
+.selectize-input,
+.selectize-input input {
+  color: #303030;
+  font-family: inherit;
+  font-size: 13px;
+  line-height: 18px;
+  -webkit-font-smoothing: inherit;
+}
+.selectize-input,
+.selectize-control.single .selectize-input.input-active {
+  background: #ffffff;
+  cursor: text;
+  display: inline-block;
+}
+.selectize-input {
+  border: 1px solid #d0d0d0;
+  padding: 8px 8px;
+  display: inline-block;
+  width: 100%;
+  overflow: hidden;
+  position: relative;
+  z-index: 1;
+  -webkit-box-sizing: border-box;
+  -moz-box-sizing: border-box;
+  box-sizing: border-box;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.1);
+  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.1);
+  -webkit-border-radius: 3px;
+  -moz-border-radius: 3px;
+  border-radius: 3px;
+}
+.selectize-control.multi .selectize-input.has-items {
+  padding: 6px 8px 3px;
+}
+.selectize-input.full {
+  background-color: #ffffff;
+}
+.selectize-input.disabled,
+.selectize-input.disabled * {
+  cursor: default !important;
+}
+.selectize-input.focus {
+  -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15);
+  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15);
+}
+.selectize-input.dropdown-active {
+  -webkit-border-radius: 3px 3px 0 0;
+  -moz-border-radius: 3px 3px 0 0;
+  border-radius: 3px 3px 0 0;
+}
+.selectize-input > * {
+  vertical-align: baseline;
+  display: -moz-inline-stack;
+  display: inline-block;
+  zoom: 1;
+  *display: inline;
+}
+.selectize-control.multi .selectize-input > div {
+  cursor: pointer;
+  margin: 0 3px 3px 0;
+  padding: 2px 6px;
+  background: #f2f2f2;
+  color: #303030;
+  border: 0 solid #d0d0d0;
+}
+.selectize-control.multi .selectize-input > div.active {
+  background: #e8e8e8;
+  color: #303030;
+  border: 0 solid #cacaca;
+}
+.selectize-control.multi .selectize-input.disabled > div,
+.selectize-control.multi .selectize-input.disabled > div.active {
+  color: #7d7d7d;
+  background: #ffffff;
+  border: 0 solid #ffffff;
+}
+.selectize-input > input {
+  display: inline-block !important;
+  padding: 0 !important;
+  min-height: 0 !important;
+  max-height: none !important;
+  max-width: 100% !important;
+  margin: 0 2px 0 0 !important;
+  text-indent: 0 !important;
+  border: 0 none !important;
+  background: none !important;
+  line-height: inherit !important;
+  -webkit-user-select: auto !important;
+  -webkit-box-shadow: none !important;
+  box-shadow: none !important;
+}
+.selectize-input > input::-ms-clear {
+  display: none;
+}
+.selectize-input > input:focus {
+  outline: none !important;
+}
+.selectize-input::after {
+  content: ' ';
+  display: block;
+  clear: left;
+}
+.selectize-input.dropdown-active::before {
+  content: ' ';
+  display: block;
+  position: absolute;
+  background: #f0f0f0;
+  height: 1px;
+  bottom: 0;
+  left: 0;
+  right: 0;
+}
+.selectize-dropdown {
+  position: absolute;
+  z-index: 10;
+  border: 1px solid #d0d0d0;
+  background: #ffffff;
+  margin: -1px 0 0 0;
+  border-top: 0 none;
+  -webkit-box-sizing: border-box;
+  -moz-box-sizing: border-box;
+  box-sizing: border-box;
+  -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+  -webkit-border-radius: 0 0 3px 3px;
+  -moz-border-radius: 0 0 3px 3px;
+  border-radius: 0 0 3px 3px;
+}
+.selectize-dropdown [data-selectable] {
+  cursor: pointer;
+  overflow: hidden;
+}
+.selectize-dropdown [data-selectable] .highlight {
+  background: rgba(125, 168, 208, 0.2);
+  -webkit-border-radius: 1px;
+  -moz-border-radius: 1px;
+  border-radius: 1px;
+}
+.selectize-dropdown [data-selectable],
+.selectize-dropdown .optgroup-header {
+  padding: 5px 8px;
+}
+.selectize-dropdown .optgroup:first-child .optgroup-header {
+  border-top: 0 none;
+}
+.selectize-dropdown .optgroup-header {
+  color: #303030;
+  background: #ffffff;
+  cursor: default;
+}
+.selectize-dropdown .active {
+  background-color: #f5fafd;
+  color: #495c68;
+}
+.selectize-dropdown .active.create {
+  color: #495c68;
+}
+.selectize-dropdown .create {
+  color: rgba(48, 48, 48, 0.5);
+}
+.selectize-dropdown-content {
+  overflow-y: auto;
+  overflow-x: hidden;
+  max-height: 200px;
+}
+.selectize-control.single .selectize-input,
+.selectize-control.single .selectize-input input {
+  cursor: pointer;
+}
+.selectize-control.single .selectize-input.input-active,
+.selectize-control.single .selectize-input.input-active input {
+  cursor: text;
+}
+.selectize-control.single .selectize-input:after {
+  content: ' ';
+  display: block;
+  position: absolute;
+  top: 50%;
+  right: 15px;
+  margin-top: -3px;
+  width: 0;
+  height: 0;
+  border-style: solid;
+  border-width: 5px 5px 0 5px;
+  border-color: #808080 transparent transparent transparent;
+}
+.selectize-control.single .selectize-input.dropdown-active:after {
+  margin-top: -4px;
+  border-width: 0 5px 5px 5px;
+  border-color: transparent transparent #808080 transparent;
+}
+.selectize-control.rtl.single .selectize-input:after {
+  left: 15px;
+  right: auto;
+}
+.selectize-control.rtl .selectize-input > input {
+  margin: 0 4px 0 -2px !important;
+}
+.selectize-control .selectize-input.disabled {
+  opacity: 0.5;
+  background-color: #fafafa;
+}
diff --git a/pgcommitfest/commitfest/static/commitfest/css/selectize.default.css b/pgcommitfest/commitfest/static/commitfest/css/selectize.default.css
new file mode 100644 (file)
index 0000000..4e4a7a1
--- /dev/null
@@ -0,0 +1,393 @@
+/**
+ * selectize.default.css (v0.12.2) - Default Theme
+ * Copyright (c) 2013–2015 Brian Reavis & contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
+ * file except in compliance with the License. You may obtain a copy of the License at:
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
+ * ANY KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ *
+ * @author Brian Reavis <[email protected]>
+ */
+.selectize-control.plugin-drag_drop.multi > .selectize-input > div.ui-sortable-placeholder {
+  visibility: visible !important;
+  background: #f2f2f2 !important;
+  background: rgba(0, 0, 0, 0.06) !important;
+  border: 0 none !important;
+  -webkit-box-shadow: inset 0 0 12px 4px #ffffff;
+  box-shadow: inset 0 0 12px 4px #ffffff;
+}
+.selectize-control.plugin-drag_drop .ui-sortable-placeholder::after {
+  content: '!';
+  visibility: hidden;
+}
+.selectize-control.plugin-drag_drop .ui-sortable-helper {
+  -webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
+  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
+}
+.selectize-dropdown-header {
+  position: relative;
+  padding: 5px 8px;
+  border-bottom: 1px solid #d0d0d0;
+  background: #f8f8f8;
+  -webkit-border-radius: 3px 3px 0 0;
+  -moz-border-radius: 3px 3px 0 0;
+  border-radius: 3px 3px 0 0;
+}
+.selectize-dropdown-header-close {
+  position: absolute;
+  right: 8px;
+  top: 50%;
+  color: #303030;
+  opacity: 0.4;
+  margin-top: -12px;
+  line-height: 20px;
+  font-size: 20px !important;
+}
+.selectize-dropdown-header-close:hover {
+  color: #000000;
+}
+.selectize-dropdown.plugin-optgroup_columns .optgroup {
+  border-right: 1px solid #f2f2f2;
+  border-top: 0 none;
+  float: left;
+  -webkit-box-sizing: border-box;
+  -moz-box-sizing: border-box;
+  box-sizing: border-box;
+}
+.selectize-dropdown.plugin-optgroup_columns .optgroup:last-child {
+  border-right: 0 none;
+}
+.selectize-dropdown.plugin-optgroup_columns .optgroup:before {
+  display: none;
+}
+.selectize-dropdown.plugin-optgroup_columns .optgroup-header {
+  border-top: 0 none;
+}
+.selectize-control.plugin-remove_button [data-value] {
+  position: relative;
+  padding-right: 24px !important;
+}
+.selectize-control.plugin-remove_button [data-value] .remove {
+  z-index: 1;
+  /* fixes ie bug (see #392) */
+  position: absolute;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  width: 17px;
+  text-align: center;
+  font-weight: bold;
+  font-size: 12px;
+  color: inherit;
+  text-decoration: none;
+  vertical-align: middle;
+  display: inline-block;
+  padding: 2px 0 0 0;
+  border-left: 1px solid #0073bb;
+  -webkit-border-radius: 0 2px 2px 0;
+  -moz-border-radius: 0 2px 2px 0;
+  border-radius: 0 2px 2px 0;
+  -webkit-box-sizing: border-box;
+  -moz-box-sizing: border-box;
+  box-sizing: border-box;
+}
+.selectize-control.plugin-remove_button [data-value] .remove:hover {
+  background: rgba(0, 0, 0, 0.05);
+}
+.selectize-control.plugin-remove_button [data-value].active .remove {
+  border-left-color: #00578d;
+}
+.selectize-control.plugin-remove_button .disabled [data-value] .remove:hover {
+  background: none;
+}
+.selectize-control.plugin-remove_button .disabled [data-value] .remove {
+  border-left-color: #aaaaaa;
+}
+.selectize-control.plugin-remove_button .remove-single {
+  position: absolute;
+  right: 28px;
+  top: 6px;
+  font-size: 23px;
+}
+.selectize-control {
+  position: relative;
+}
+.selectize-dropdown,
+.selectize-input,
+.selectize-input input {
+  color: #303030;
+  font-family: inherit;
+  font-size: 13px;
+  line-height: 18px;
+  -webkit-font-smoothing: inherit;
+}
+.selectize-input,
+.selectize-control.single .selectize-input.input-active {
+  background: #ffffff;
+  cursor: text;
+  display: inline-block;
+}
+.selectize-input {
+  border: 1px solid #d0d0d0;
+  padding: 8px 8px;
+  display: inline-block;
+  width: 100%;
+  overflow: hidden;
+  position: relative;
+  z-index: 1;
+  -webkit-box-sizing: border-box;
+  -moz-box-sizing: border-box;
+  box-sizing: border-box;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.1);
+  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.1);
+  -webkit-border-radius: 3px;
+  -moz-border-radius: 3px;
+  border-radius: 3px;
+}
+.selectize-control.multi .selectize-input.has-items {
+  padding: 5px 8px 2px;
+}
+.selectize-input.full {
+  background-color: #ffffff;
+}
+.selectize-input.disabled,
+.selectize-input.disabled * {
+  cursor: default !important;
+}
+.selectize-input.focus {
+  -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15);
+  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15);
+}
+.selectize-input.dropdown-active {
+  -webkit-border-radius: 3px 3px 0 0;
+  -moz-border-radius: 3px 3px 0 0;
+  border-radius: 3px 3px 0 0;
+}
+.selectize-input > * {
+  vertical-align: baseline;
+  display: -moz-inline-stack;
+  display: inline-block;
+  zoom: 1;
+  *display: inline;
+}
+.selectize-control.multi .selectize-input > div {
+  cursor: pointer;
+  margin: 0 3px 3px 0;
+  padding: 2px 6px;
+  background: #1da7ee;
+  color: #ffffff;
+  border: 1px solid #0073bb;
+}
+.selectize-control.multi .selectize-input > div.active {
+  background: #92c836;
+  color: #ffffff;
+  border: 1px solid #00578d;
+}
+.selectize-control.multi .selectize-input.disabled > div,
+.selectize-control.multi .selectize-input.disabled > div.active {
+  color: #ffffff;
+  background: #d2d2d2;
+  border: 1px solid #aaaaaa;
+}
+.selectize-input > input {
+  display: inline-block !important;
+  padding: 0 !important;
+  min-height: 0 !important;
+  max-height: none !important;
+  max-width: 100% !important;
+  margin: 0 1px !important;
+  text-indent: 0 !important;
+  border: 0 none !important;
+  background: none !important;
+  line-height: inherit !important;
+  -webkit-user-select: auto !important;
+  -webkit-box-shadow: none !important;
+  box-shadow: none !important;
+}
+.selectize-input > input::-ms-clear {
+  display: none;
+}
+.selectize-input > input:focus {
+  outline: none !important;
+}
+.selectize-input::after {
+  content: ' ';
+  display: block;
+  clear: left;
+}
+.selectize-input.dropdown-active::before {
+  content: ' ';
+  display: block;
+  position: absolute;
+  background: #f0f0f0;
+  height: 1px;
+  bottom: 0;
+  left: 0;
+  right: 0;
+}
+.selectize-dropdown {
+  position: absolute;
+  z-index: 10;
+  border: 1px solid #d0d0d0;
+  background: #ffffff;
+  margin: -1px 0 0 0;
+  border-top: 0 none;
+  -webkit-box-sizing: border-box;
+  -moz-box-sizing: border-box;
+  box-sizing: border-box;
+  -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+  -webkit-border-radius: 0 0 3px 3px;
+  -moz-border-radius: 0 0 3px 3px;
+  border-radius: 0 0 3px 3px;
+}
+.selectize-dropdown [data-selectable] {
+  cursor: pointer;
+  overflow: hidden;
+}
+.selectize-dropdown [data-selectable] .highlight {
+  background: rgba(125, 168, 208, 0.2);
+  -webkit-border-radius: 1px;
+  -moz-border-radius: 1px;
+  border-radius: 1px;
+}
+.selectize-dropdown [data-selectable],
+.selectize-dropdown .optgroup-header {
+  padding: 5px 8px;
+}
+.selectize-dropdown .optgroup:first-child .optgroup-header {
+  border-top: 0 none;
+}
+.selectize-dropdown .optgroup-header {
+  color: #303030;
+  background: #ffffff;
+  cursor: default;
+}
+.selectize-dropdown .active {
+  background-color: #f5fafd;
+  color: #495c68;
+}
+.selectize-dropdown .active.create {
+  color: #495c68;
+}
+.selectize-dropdown .create {
+  color: rgba(48, 48, 48, 0.5);
+}
+.selectize-dropdown-content {
+  overflow-y: auto;
+  overflow-x: hidden;
+  max-height: 200px;
+}
+.selectize-control.single .selectize-input,
+.selectize-control.single .selectize-input input {
+  cursor: pointer;
+}
+.selectize-control.single .selectize-input.input-active,
+.selectize-control.single .selectize-input.input-active input {
+  cursor: text;
+}
+.selectize-control.single .selectize-input:after {
+  content: ' ';
+  display: block;
+  position: absolute;
+  top: 50%;
+  right: 15px;
+  margin-top: -3px;
+  width: 0;
+  height: 0;
+  border-style: solid;
+  border-width: 5px 5px 0 5px;
+  border-color: #808080 transparent transparent transparent;
+}
+.selectize-control.single .selectize-input.dropdown-active:after {
+  margin-top: -4px;
+  border-width: 0 5px 5px 5px;
+  border-color: transparent transparent #808080 transparent;
+}
+.selectize-control.rtl.single .selectize-input:after {
+  left: 15px;
+  right: auto;
+}
+.selectize-control.rtl .selectize-input > input {
+  margin: 0 4px 0 -2px !important;
+}
+.selectize-control .selectize-input.disabled {
+  opacity: 0.5;
+  background-color: #fafafa;
+}
+.selectize-control.multi .selectize-input.has-items {
+  padding-left: 5px;
+  padding-right: 5px;
+}
+.selectize-control.multi .selectize-input.disabled [data-value] {
+  color: #999;
+  text-shadow: none;
+  background: none;
+  -webkit-box-shadow: none;
+  box-shadow: none;
+}
+.selectize-control.multi .selectize-input.disabled [data-value],
+.selectize-control.multi .selectize-input.disabled [data-value] .remove {
+  border-color: #e6e6e6;
+}
+.selectize-control.multi .selectize-input.disabled [data-value] .remove {
+  background: none;
+}
+.selectize-control.multi .selectize-input [data-value] {
+  text-shadow: 0 1px 0 rgba(0, 51, 83, 0.3);
+  -webkit-border-radius: 3px;
+  -moz-border-radius: 3px;
+  border-radius: 3px;
+  background-color: #1b9dec;
+  background-image: -moz-linear-gradient(top, #1da7ee, #178ee9);
+  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#1da7ee), to(#178ee9));
+  background-image: -webkit-linear-gradient(top, #1da7ee, #178ee9);
+  background-image: -o-linear-gradient(top, #1da7ee, #178ee9);
+  background-image: linear-gradient(to bottom, #1da7ee, #178ee9);
+  background-repeat: repeat-x;
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff1da7ee', endColorstr='#ff178ee9', GradientType=0);
+  -webkit-box-shadow: 0 1px 0 rgba(0,0,0,0.2),inset 0 1px rgba(255,255,255,0.03);
+  box-shadow: 0 1px 0 rgba(0,0,0,0.2),inset 0 1px rgba(255,255,255,0.03);
+}
+.selectize-control.multi .selectize-input [data-value].active {
+  background-color: #0085d4;
+  background-image: -moz-linear-gradient(top, #008fd8, #0075cf);
+  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#008fd8), to(#0075cf));
+  background-image: -webkit-linear-gradient(top, #008fd8, #0075cf);
+  background-image: -o-linear-gradient(top, #008fd8, #0075cf);
+  background-image: linear-gradient(to bottom, #008fd8, #0075cf);
+  background-repeat: repeat-x;
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff008fd8', endColorstr='#ff0075cf', GradientType=0);
+}
+.selectize-control.single .selectize-input {
+  -webkit-box-shadow: 0 1px 0 rgba(0,0,0,0.05), inset 0 1px 0 rgba(255,255,255,0.8);
+  box-shadow: 0 1px 0 rgba(0,0,0,0.05), inset 0 1px 0 rgba(255,255,255,0.8);
+  background-color: #f9f9f9;
+  background-image: -moz-linear-gradient(top, #fefefe, #f2f2f2);
+  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fefefe), to(#f2f2f2));
+  background-image: -webkit-linear-gradient(top, #fefefe, #f2f2f2);
+  background-image: -o-linear-gradient(top, #fefefe, #f2f2f2);
+  background-image: linear-gradient(to bottom, #fefefe, #f2f2f2);
+  background-repeat: repeat-x;
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffefefe', endColorstr='#fff2f2f2', GradientType=0);
+}
+.selectize-control.single .selectize-input,
+.selectize-dropdown.single {
+  border-color: #b8b8b8;
+}
+.selectize-dropdown .optgroup-header {
+  padding-top: 7px;
+  font-weight: bold;
+  font-size: 0.85em;
+}
+.selectize-dropdown .optgroup {
+  border-top: 1px solid #f0f0f0;
+}
+.selectize-dropdown .optgroup:first-child {
+  border-top: 0 none;
+}
diff --git a/pgcommitfest/commitfest/static/commitfest/js/selectize.min.js b/pgcommitfest/commitfest/static/commitfest/js/selectize.min.js
new file mode 100644 (file)
index 0000000..305d531
--- /dev/null
@@ -0,0 +1,3 @@
+/*! selectize.js - v0.12.2 | https://github.com/selectize/selectize.js | Apache License (v2) */
+!function(a,b){"function"==typeof define&&define.amd?define("sifter",b):"object"==typeof exports?module.exports=b():a.Sifter=b()}(this,function(){var a=function(a,b){this.items=a,this.settings=b||{diacritics:!0}};a.prototype.tokenize=function(a){if(a=e(String(a||"").toLowerCase()),!a||!a.length)return[];var b,c,d,g,i=[],j=a.split(/ +/);for(b=0,c=j.length;b<c;b++){if(d=f(j[b]),this.settings.diacritics)for(g in h)h.hasOwnProperty(g)&&(d=d.replace(new RegExp(g,"g"),h[g]));i.push({string:j[b],regex:new RegExp(d,"i")})}return i},a.prototype.iterator=function(a,b){var c;c=g(a)?Array.prototype.forEach||function(a){for(var b=0,c=this.length;b<c;b++)a(this[b],b,this)}:function(a){for(var b in this)this.hasOwnProperty(b)&&a(this[b],b,this)},c.apply(a,[b])},a.prototype.getScoreFunction=function(a,b){var c,e,f,g,h;c=this,a=c.prepareSearch(a,b),f=a.tokens,e=a.options.fields,g=f.length,h=a.options.nesting;var i=function(a,b){var c,d;return a?(a=String(a||""),d=a.search(b.regex),d===-1?0:(c=b.string.length/a.length,0===d&&(c+=.5),c)):0},j=function(){var a=e.length;return a?1===a?function(a,b){return i(d(b,e[0],h),a)}:function(b,c){for(var f=0,g=0;f<a;f++)g+=i(d(c,e[f],h),b);return g/a}:function(){return 0}}();return g?1===g?function(a){return j(f[0],a)}:"and"===a.options.conjunction?function(a){for(var b,c=0,d=0;c<g;c++){if(b=j(f[c],a),b<=0)return 0;d+=b}return d/g}:function(a){for(var b=0,c=0;b<g;b++)c+=j(f[b],a);return c/g}:function(){return 0}},a.prototype.getSortFunction=function(a,c){var e,f,g,h,i,j,k,l,m,n,o;if(g=this,a=g.prepareSearch(a,c),o=!a.query&&c.sort_empty||c.sort,m=function(a,b){return"$score"===a?b.score:d(g.items[b.id],a,c.nesting)},i=[],o)for(e=0,f=o.length;e<f;e++)(a.query||"$score"!==o[e].field)&&i.push(o[e]);if(a.query){for(n=!0,e=0,f=i.length;e<f;e++)if("$score"===i[e].field){n=!1;break}n&&i.unshift({field:"$score",direction:"desc"})}else for(e=0,f=i.length;e<f;e++)if("$score"===i[e].field){i.splice(e,1);break}for(l=[],e=0,f=i.length;e<f;e++)l.push("desc"===i[e].direction?-1:1);return j=i.length,j?1===j?(h=i[0].field,k=l[0],function(a,c){return k*b(m(h,a),m(h,c))}):function(a,c){var d,e,f;for(d=0;d<j;d++)if(f=i[d].field,e=l[d]*b(m(f,a),m(f,c)))return e;return 0}:null},a.prototype.prepareSearch=function(a,b){if("object"==typeof a)return a;b=c({},b);var d=b.fields,e=b.sort,f=b.sort_empty;return d&&!g(d)&&(b.fields=[d]),e&&!g(e)&&(b.sort=[e]),f&&!g(f)&&(b.sort_empty=[f]),{options:b,query:String(a||"").toLowerCase(),tokens:this.tokenize(a),total:0,items:[]}},a.prototype.search=function(a,b){var c,d,e,f,g=this;return d=this.prepareSearch(a,b),b=d.options,a=d.query,f=b.score||g.getScoreFunction(d),a.length?g.iterator(g.items,function(a,e){c=f(a),(b.filter===!1||c>0)&&d.items.push({score:c,id:e})}):g.iterator(g.items,function(a,b){d.items.push({score:1,id:b})}),e=g.getSortFunction(d,b),e&&d.items.sort(e),d.total=d.items.length,"number"==typeof b.limit&&(d.items=d.items.slice(0,b.limit)),d};var b=function(a,b){return"number"==typeof a&&"number"==typeof b?a>b?1:a<b?-1:0:(a=i(String(a||"")),b=i(String(b||"")),a>b?1:b>a?-1:0)},c=function(a,b){var c,d,e,f;for(c=1,d=arguments.length;c<d;c++)if(f=arguments[c])for(e in f)f.hasOwnProperty(e)&&(a[e]=f[e]);return a},d=function(a,b,c){if(a&&b){if(!c)return a[b];for(var d=b.split(".");d.length&&(a=a[d.shift()]););return a}},e=function(a){return(a+"").replace(/^\s+|\s+$|/g,"")},f=function(a){return(a+"").replace(/([.?*+^$[\]\\(){}|-])/g,"\\$1")},g=Array.isArray||"undefined"!=typeof $&&$.isArray||function(a){return"[object Array]"===Object.prototype.toString.call(a)},h={a:"[aḀḁĂăÂâǍǎȺⱥȦȧẠạÄäÀàÁáĀāÃãÅåąĄÃąĄ]",b:"[b␢βΒB฿𐌁ᛒ]",c:"[cĆćĈĉČčĊċC̄c̄ÇçḈḉȻȼƇƈɕᴄCc]",d:"[dĎďḊḋḐḑḌḍḒḓḎḏĐđD̦d̦ƉɖƊɗƋƌᵭᶁᶑȡᴅDdð]",e:"[eÉéÈèÊêḘḙĚěĔĕẼẽḚḛẺẻĖėËëĒēȨȩĘęᶒɆɇȄȅẾếỀềỄễỂểḜḝḖḗḔḕȆȇẸẹỆệⱸᴇEeɘǝƏƐε]",f:"[fƑƒḞḟ]",g:"[gɢ₲ǤǥĜĝĞğĢģƓɠĠġ]",h:"[hĤĥĦħḨḩẖẖḤḥḢḣɦʰǶƕ]",i:"[iÍíÌìĬĭÎîǏǐÏïḮḯĨĩĮįĪīỈỉȈȉȊȋỊịḬḭƗɨɨ̆ᵻᶖİiIıɪIi]",j:"[jȷĴĵɈɉʝɟʲ]",k:"[kƘƙꝀꝁḰḱǨǩḲḳḴḵκϰ₭]",l:"[lŁłĽľĻļĹĺḶḷḸḹḼḽḺḻĿŀȽƚⱠⱡⱢɫɬᶅɭȴʟLl]",n:"[nŃńǸǹŇňÑñṄṅŅņṆṇṊṋṈṉN̈n̈ƝɲȠƞᵰᶇɳȵɴNnŊŋ]",o:"[oØøÖöÓóÒòÔôǑǒŐőŎŏȮȯỌọƟɵƠơỎỏŌōÕõǪǫȌȍՕօ]",p:"[pṔṕṖṗⱣᵽƤƥᵱ]",q:"[qꝖꝗʠɊɋꝘꝙq̃]",r:"[rŔŕɌɍŘřŖŗṘṙȐȑȒȓṚṛⱤɽ]",s:"[sŚśṠṡṢṣꞨꞩŜŝŠšŞşȘșS̈s̈]",t:"[tŤťṪṫŢţṬṭƮʈȚțṰṱṮṯƬƭ]",u:"[uŬŭɄʉỤụÜüÚúÙùÛûǓǔŰűŬŭƯưỦủŪūŨũŲųȔȕ∪]",v:"[vṼṽṾṿƲʋꝞꝟⱱʋ]",w:"[wẂẃẀẁŴŵẄẅẆẇẈẉ]",x:"[xẌẍẊẋχ]",y:"[yÝýỲỳŶŷŸÿỸỹẎẏỴỵɎɏƳƴ]",z:"[zŹźẐẑŽžŻżẒẓẔẕƵƶ]"},i=function(){var a,b,c,d,e="",f={};for(c in h)if(h.hasOwnProperty(c))for(d=h[c].substring(2,h[c].length-1),e+=d,a=0,b=d.length;a<b;a++)f[d.charAt(a)]=c;var g=new RegExp("["+e+"]","g");return function(a){return a.replace(g,function(a){return f[a]}).toLowerCase()}}();return a}),function(a,b){"function"==typeof define&&define.amd?define("microplugin",b):"object"==typeof exports?module.exports=b():a.MicroPlugin=b()}(this,function(){var a={};a.mixin=function(a){a.plugins={},a.prototype.initializePlugins=function(a){var c,d,e,f=this,g=[];if(f.plugins={names:[],settings:{},requested:{},loaded:{}},b.isArray(a))for(c=0,d=a.length;c<d;c++)"string"==typeof a[c]?g.push(a[c]):(f.plugins.settings[a[c].name]=a[c].options,g.push(a[c].name));else if(a)for(e in a)a.hasOwnProperty(e)&&(f.plugins.settings[e]=a[e],g.push(e));for(;g.length;)f.require(g.shift())},a.prototype.loadPlugin=function(b){var c=this,d=c.plugins,e=a.plugins[b];if(!a.plugins.hasOwnProperty(b))throw new Error('Unable to find "'+b+'" plugin');d.requested[b]=!0,d.loaded[b]=e.fn.apply(c,[c.plugins.settings[b]||{}]),d.names.push(b)},a.prototype.require=function(a){var b=this,c=b.plugins;if(!b.plugins.loaded.hasOwnProperty(a)){if(c.requested[a])throw new Error('Plugin has circular dependency ("'+a+'")');b.loadPlugin(a)}return c.loaded[a]},a.define=function(b,c){a.plugins[b]={name:b,fn:c}}};var b={isArray:Array.isArray||function(a){return"[object Array]"===Object.prototype.toString.call(a)}};return a}),function(a,b){"function"==typeof define&&define.amd?define("selectize",["jquery","sifter","microplugin"],b):"object"==typeof exports?module.exports=b(require("jquery"),require("sifter"),require("microplugin")):a.Selectize=b(a.jQuery,a.Sifter,a.MicroPlugin)}(this,function(a,b,c){"use strict";var d=function(a,b){if("string"!=typeof b||b.length){var c="string"==typeof b?new RegExp(b,"i"):b,d=function(a){var b=0;if(3===a.nodeType){var e=a.data.search(c);if(e>=0&&a.data.length>0){var f=a.data.match(c),g=document.createElement("span");g.className="highlight";var h=a.splitText(e),i=(h.splitText(f[0].length),h.cloneNode(!0));g.appendChild(i),h.parentNode.replaceChild(g,h),b=1}}else if(1===a.nodeType&&a.childNodes&&!/(script|style)/i.test(a.tagName))for(var j=0;j<a.childNodes.length;++j)j+=d(a.childNodes[j]);return b};return a.each(function(){d(this)})}},e=function(){};e.prototype={on:function(a,b){this._events=this._events||{},this._events[a]=this._events[a]||[],this._events[a].push(b)},off:function(a,b){var c=arguments.length;return 0===c?delete this._events:1===c?delete this._events[a]:(this._events=this._events||{},void(a in this._events!=!1&&this._events[a].splice(this._events[a].indexOf(b),1)))},trigger:function(a){if(this._events=this._events||{},a in this._events!=!1)for(var b=0;b<this._events[a].length;b++)this._events[a][b].apply(this,Array.prototype.slice.call(arguments,1))}},e.mixin=function(a){for(var b=["on","off","trigger"],c=0;c<b.length;c++)a.prototype[b[c]]=e.prototype[b[c]]};var f=/Mac/.test(navigator.userAgent),g=65,h=13,i=27,j=37,k=38,l=80,m=39,n=40,o=78,p=8,q=46,r=16,s=f?91:17,t=f?18:17,u=9,v=1,w=2,x=!/android/i.test(window.navigator.userAgent)&&!!document.createElement("form").validity,y=function(a){return"undefined"!=typeof a},z=function(a){return"undefined"==typeof a||null===a?null:"boolean"==typeof a?a?"1":"0":a+""},A=function(a){return(a+"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")},B={};B.before=function(a,b,c){var d=a[b];a[b]=function(){return c.apply(a,arguments),d.apply(a,arguments)}},B.after=function(a,b,c){var d=a[b];a[b]=function(){var b=d.apply(a,arguments);return c.apply(a,arguments),b}};var C=function(a){var b=!1;return function(){b||(b=!0,a.apply(this,arguments))}},D=function(a,b){var c;return function(){var d=this,e=arguments;window.clearTimeout(c),c=window.setTimeout(function(){a.apply(d,e)},b)}},E=function(a,b,c){var d,e=a.trigger,f={};a.trigger=function(){var c=arguments[0];return b.indexOf(c)===-1?e.apply(a,arguments):void(f[c]=arguments)},c.apply(a,[]),a.trigger=e;for(d in f)f.hasOwnProperty(d)&&e.apply(a,f[d])},F=function(a,b,c,d){a.on(b,c,function(b){for(var c=b.target;c&&c.parentNode!==a[0];)c=c.parentNode;return b.currentTarget=c,d.apply(this,[b])})},G=function(a){var b={};if("selectionStart"in a)b.start=a.selectionStart,b.length=a.selectionEnd-b.start;else if(document.selection){a.focus();var c=document.selection.createRange(),d=document.selection.createRange().text.length;c.moveStart("character",-a.value.length),b.start=c.text.length-d,b.length=d}return b},H=function(a,b,c){var d,e,f={};if(c)for(d=0,e=c.length;d<e;d++)f[c[d]]=a.css(c[d]);else f=a.css();b.css(f)},I=function(b,c){if(!b)return 0;var d=a("<test>").css({position:"absolute",top:-99999,left:-99999,width:"auto",padding:0,whiteSpace:"pre"}).text(b).appendTo("body");H(c,d,["letterSpacing","fontSize","fontFamily","fontWeight","textTransform"]);var e=d.width();return d.remove(),e},J=function(a){var b=null,c=function(c,d){var e,f,g,h,i,j,k,l;c=c||window.event||{},d=d||{},c.metaKey||c.altKey||(d.force||a.data("grow")!==!1)&&(e=a.val(),c.type&&"keydown"===c.type.toLowerCase()&&(f=c.keyCode,g=f>=97&&f<=122||f>=65&&f<=90||f>=48&&f<=57||32===f,f===q||f===p?(l=G(a[0]),l.length?e=e.substring(0,l.start)+e.substring(l.start+l.length):f===p&&l.start?e=e.substring(0,l.start-1)+e.substring(l.start+1):f===q&&"undefined"!=typeof l.start&&(e=e.substring(0,l.start)+e.substring(l.start+1))):g&&(j=c.shiftKey,k=String.fromCharCode(c.keyCode),k=j?k.toUpperCase():k.toLowerCase(),e+=k)),h=a.attr("placeholder"),!e&&h&&(e=h),i=I(e,a)+4,i!==b&&(b=i,a.width(i),a.triggerHandler("resize")))};a.on("keydown keyup update blur",c),c()},K=function(a){var b=document.createElement("div");return b.appendChild(a.cloneNode(!0)),b.innerHTML},L=function(c,d){var e,f,g,h,i=this;h=c[0],h.selectize=i;var j=window.getComputedStyle&&window.getComputedStyle(h,null);if(g=j?j.getPropertyValue("direction"):h.currentStyle&&h.currentStyle.direction,g=g||c.parents("[dir]:first").attr("dir")||"",a.extend(i,{order:0,settings:d,$input:c,tabIndex:c.attr("tabindex")||"",tagType:"select"===h.tagName.toLowerCase()?v:w,rtl:/rtl/i.test(g),eventNS:".selectize"+ ++L.count,highlightedValue:null,isOpen:!1,isDisabled:!1,isRequired:c.is("[required]"),isInvalid:!1,isLocked:!1,isFocused:!1,isInputHidden:!1,isSetup:!1,isShiftDown:!1,isCmdDown:!1,isCtrlDown:!1,ignoreFocus:!1,ignoreBlur:!1,ignoreHover:!1,hasOptions:!1,currentResults:null,lastValue:"",caretPos:0,loading:0,loadedSearches:{},$activeOption:null,$activeItems:[],optgroups:{},options:{},userOptions:{},items:[],renderCache:{},onSearchChange:null===d.loadThrottle?i.onSearchChange:D(i.onSearchChange,d.loadThrottle)}),i.sifter=new b(this.options,{diacritics:d.diacritics}),i.settings.options){for(e=0,f=i.settings.options.length;e<f;e++)i.registerOption(i.settings.options[e]);delete i.settings.options}if(i.settings.optgroups){for(e=0,f=i.settings.optgroups.length;e<f;e++)i.registerOptionGroup(i.settings.optgroups[e]);delete i.settings.optgroups}i.settings.mode=i.settings.mode||(1===i.settings.maxItems?"single":"multi"),"boolean"!=typeof i.settings.hideSelected&&(i.settings.hideSelected="multi"===i.settings.mode),i.initializePlugins(i.settings.plugins),i.setupCallbacks(),i.setupTemplates(),i.setup()};return e.mixin(L),c.mixin(L),a.extend(L.prototype,{setup:function(){var b,c,d,e,g,h,i,j,k,l=this,m=l.settings,n=l.eventNS,o=a(window),p=a(document),q=l.$input;if(i=l.settings.mode,j=q.attr("class")||"",b=a("<div>").addClass(m.wrapperClass).addClass(j).addClass(i),c=a("<div>").addClass(m.inputClass).addClass("items").appendTo(b),d=a('<input type="text" autocomplete="off" />').appendTo(c).attr("tabindex",q.is(":disabled")?"-1":l.tabIndex),h=a(m.dropdownParent||b),e=a("<div>").addClass(m.dropdownClass).addClass(i).hide().appendTo(h),g=a("<div>").addClass(m.dropdownContentClass).appendTo(e),l.settings.copyClassesToDropdown&&e.addClass(j),b.css({width:q[0].style.width}),l.plugins.names.length&&(k="plugin-"+l.plugins.names.join(" plugin-"),b.addClass(k),e.addClass(k)),(null===m.maxItems||m.maxItems>1)&&l.tagType===v&&q.attr("multiple","multiple"),l.settings.placeholder&&d.attr("placeholder",m.placeholder),!l.settings.splitOn&&l.settings.delimiter){var u=l.settings.delimiter.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&");l.settings.splitOn=new RegExp("\\s*"+u+"+\\s*")}q.attr("autocorrect")&&d.attr("autocorrect",q.attr("autocorrect")),q.attr("autocapitalize")&&d.attr("autocapitalize",q.attr("autocapitalize")),l.$wrapper=b,l.$control=c,l.$control_input=d,l.$dropdown=e,l.$dropdown_content=g,e.on("mouseenter","[data-selectable]",function(){return l.onOptionHover.apply(l,arguments)}),e.on("mousedown click","[data-selectable]",function(){return l.onOptionSelect.apply(l,arguments)}),F(c,"mousedown","*:not(input)",function(){return l.onItemSelect.apply(l,arguments)}),J(d),c.on({mousedown:function(){return l.onMouseDown.apply(l,arguments)},click:function(){return l.onClick.apply(l,arguments)}}),d.on({mousedown:function(a){a.stopPropagation()},keydown:function(){return l.onKeyDown.apply(l,arguments)},keyup:function(){return l.onKeyUp.apply(l,arguments)},keypress:function(){return l.onKeyPress.apply(l,arguments)},resize:function(){l.positionDropdown.apply(l,[])},blur:function(){return l.onBlur.apply(l,arguments)},focus:function(){return l.ignoreBlur=!1,l.onFocus.apply(l,arguments)},paste:function(){return l.onPaste.apply(l,arguments)}}),p.on("keydown"+n,function(a){l.isCmdDown=a[f?"metaKey":"ctrlKey"],l.isCtrlDown=a[f?"altKey":"ctrlKey"],l.isShiftDown=a.shiftKey}),p.on("keyup"+n,function(a){a.keyCode===t&&(l.isCtrlDown=!1),a.keyCode===r&&(l.isShiftDown=!1),a.keyCode===s&&(l.isCmdDown=!1)}),p.on("mousedown"+n,function(a){if(l.isFocused){if(a.target===l.$dropdown[0]||a.target.parentNode===l.$dropdown[0])return!1;l.$control.has(a.target).length||a.target===l.$control[0]||l.blur(a.target)}}),o.on(["scroll"+n,"resize"+n].join(" "),function(){l.isOpen&&l.positionDropdown.apply(l,arguments)}),o.on("mousemove"+n,function(){l.ignoreHover=!1}),this.revertSettings={$children:q.children().detach(),tabindex:q.attr("tabindex")},q.attr("tabindex",-1).hide().after(l.$wrapper),a.isArray(m.items)&&(l.setValue(m.items),delete m.items),x&&q.on("invalid"+n,function(a){a.preventDefault(),l.isInvalid=!0,l.refreshState()}),l.updateOriginalInput(),l.refreshItems(),l.refreshState(),l.updatePlaceholder(),l.isSetup=!0,q.is(":disabled")&&l.disable(),l.on("change",this.onChange),q.data("selectize",l),q.addClass("selectized"),l.trigger("initialize"),m.preload===!0&&l.onSearchChange("")},setupTemplates:function(){var b=this,c=b.settings.labelField,d=b.settings.optgroupLabelField,e={optgroup:function(a){return'<div class="optgroup">'+a.html+"</div>"},optgroup_header:function(a,b){return'<div class="optgroup-header">'+b(a[d])+"</div>"},option:function(a,b){return'<div class="option">'+b(a[c])+"</div>"},item:function(a,b){return'<div class="item">'+b(a[c])+"</div>"},option_create:function(a,b){return'<div class="create">Add <strong>'+b(a.input)+"</strong>&hellip;</div>"}};b.settings.render=a.extend({},e,b.settings.render)},setupCallbacks:function(){var a,b,c={initialize:"onInitialize",change:"onChange",item_add:"onItemAdd",item_remove:"onItemRemove",clear:"onClear",option_add:"onOptionAdd",option_remove:"onOptionRemove",option_clear:"onOptionClear",optgroup_add:"onOptionGroupAdd",optgroup_remove:"onOptionGroupRemove",optgroup_clear:"onOptionGroupClear",dropdown_open:"onDropdownOpen",dropdown_close:"onDropdownClose",type:"onType",load:"onLoad",focus:"onFocus",blur:"onBlur"};for(a in c)c.hasOwnProperty(a)&&(b=this.settings[c[a]],b&&this.on(a,b))},onClick:function(a){var b=this;b.isFocused||(b.focus(),a.preventDefault())},onMouseDown:function(b){var c=this,d=b.isDefaultPrevented();a(b.target);if(c.isFocused){if(b.target!==c.$control_input[0])return"single"===c.settings.mode?c.isOpen?c.close():c.open():d||c.setActiveItem(null),!1}else d||window.setTimeout(function(){c.focus()},0)},onChange:function(){this.$input.trigger("change")},onPaste:function(b){var c=this;c.isFull()||c.isInputHidden||c.isLocked?b.preventDefault():c.settings.splitOn&&setTimeout(function(){for(var b=a.trim(c.$control_input.val()||"").split(c.settings.splitOn),d=0,e=b.length;d<e;d++)c.createItem(b[d])},0)},onKeyPress:function(a){if(this.isLocked)return a&&a.preventDefault();var b=String.fromCharCode(a.keyCode||a.which);return this.settings.create&&"multi"===this.settings.mode&&b===this.settings.delimiter?(this.createItem(),a.preventDefault(),!1):void 0},onKeyDown:function(a){var b=(a.target===this.$control_input[0],this);if(b.isLocked)return void(a.keyCode!==u&&a.preventDefault());switch(a.keyCode){case g:if(b.isCmdDown)return void b.selectAll();break;case i:return void(b.isOpen&&(a.preventDefault(),a.stopPropagation(),b.close()));case o:if(!a.ctrlKey||a.altKey)break;case n:if(!b.isOpen&&b.hasOptions)b.open();else if(b.$activeOption){b.ignoreHover=!0;var c=b.getAdjacentOption(b.$activeOption,1);c.length&&b.setActiveOption(c,!0,!0)}return void a.preventDefault();case l:if(!a.ctrlKey||a.altKey)break;case k:if(b.$activeOption){b.ignoreHover=!0;var d=b.getAdjacentOption(b.$activeOption,-1);d.length&&b.setActiveOption(d,!0,!0)}return void a.preventDefault();case h:return void(b.isOpen&&b.$activeOption&&(b.onOptionSelect({currentTarget:b.$activeOption}),a.preventDefault()));case j:return void b.advanceSelection(-1,a);case m:return void b.advanceSelection(1,a);case u:return b.settings.selectOnTab&&b.isOpen&&b.$activeOption&&(b.onOptionSelect({currentTarget:b.$activeOption}),b.isFull()||a.preventDefault()),void(b.settings.create&&b.createItem()&&a.preventDefault());case p:case q:return void b.deleteSelection(a)}return!b.isFull()&&!b.isInputHidden||(f?a.metaKey:a.ctrlKey)?void 0:void a.preventDefault()},onKeyUp:function(a){var b=this;if(b.isLocked)return a&&a.preventDefault();var c=b.$control_input.val()||"";b.lastValue!==c&&(b.lastValue=c,b.onSearchChange(c),b.refreshOptions(),b.trigger("type",c))},onSearchChange:function(a){var b=this,c=b.settings.load;c&&(b.loadedSearches.hasOwnProperty(a)||(b.loadedSearches[a]=!0,b.load(function(d){c.apply(b,[a,d])})))},onFocus:function(a){var b=this,c=b.isFocused;return b.isDisabled?(b.blur(),a&&a.preventDefault(),!1):void(b.ignoreFocus||(b.isFocused=!0,"focus"===b.settings.preload&&b.onSearchChange(""),c||b.trigger("focus"),b.$activeItems.length||(b.showInput(),b.setActiveItem(null),b.refreshOptions(!!b.settings.openOnFocus)),b.refreshState()))},onBlur:function(a,b){var c=this;if(c.isFocused&&(c.isFocused=!1,!c.ignoreFocus)){if(!c.ignoreBlur&&document.activeElement===c.$dropdown_content[0])return c.ignoreBlur=!0,void c.onFocus(a);var d=function(){c.close(),c.setTextboxValue(""),c.setActiveItem(null),c.setActiveOption(null),c.setCaret(c.items.length),c.refreshState(),b&&b.focus(),c.ignoreFocus=!1,c.trigger("blur")};c.ignoreFocus=!0,c.settings.create&&c.settings.createOnBlur?c.createItem(null,!1,d):d()}},onOptionHover:function(a){this.ignoreHover||this.setActiveOption(a.currentTarget,!1)},onOptionSelect:function(b){var c,d,e=this;b.preventDefault&&(b.preventDefault(),b.stopPropagation()),d=a(b.currentTarget),d.hasClass("create")?e.createItem(null,function(){e.settings.closeAfterSelect&&e.close()}):(c=d.attr("data-value"),"undefined"!=typeof c&&(e.lastQuery=null,e.setTextboxValue(""),e.addItem(c),e.settings.closeAfterSelect?e.close():!e.settings.hideSelected&&b.type&&/mouse/.test(b.type)&&e.setActiveOption(e.getOption(c))))},onItemSelect:function(a){var b=this;b.isLocked||"multi"===b.settings.mode&&(a.preventDefault(),b.setActiveItem(a.currentTarget,a))},load:function(a){var b=this,c=b.$wrapper.addClass(b.settings.loadingClass);b.loading++,a.apply(b,[function(a){b.loading=Math.max(b.loading-1,0),a&&a.length&&(b.addOption(a),b.refreshOptions(b.isFocused&&!b.isInputHidden)),b.loading||c.removeClass(b.settings.loadingClass),b.trigger("load",a)}])},setTextboxValue:function(a){var b=this.$control_input,c=b.val()!==a;c&&(b.val(a).triggerHandler("update"),this.lastValue=a)},getValue:function(){return this.tagType===v&&this.$input.attr("multiple")?this.items:this.items.join(this.settings.delimiter)},setValue:function(a,b){var c=b?[]:["change"];E(this,c,function(){this.clear(b),this.addItems(a,b)})},setActiveItem:function(b,c){var d,e,f,g,h,i,j,k,l=this;if("single"!==l.settings.mode){if(b=a(b),!b.length)return a(l.$activeItems).removeClass("active"),l.$activeItems=[],void(l.isFocused&&l.showInput());if(d=c&&c.type.toLowerCase(),"mousedown"===d&&l.isShiftDown&&l.$activeItems.length){for(k=l.$control.children(".active:last"),g=Array.prototype.indexOf.apply(l.$control[0].childNodes,[k[0]]),h=Array.prototype.indexOf.apply(l.$control[0].childNodes,[b[0]]),g>h&&(j=g,g=h,h=j),e=g;e<=h;e++)i=l.$control[0].childNodes[e],l.$activeItems.indexOf(i)===-1&&(a(i).addClass("active"),l.$activeItems.push(i));c.preventDefault()}else"mousedown"===d&&l.isCtrlDown||"keydown"===d&&this.isShiftDown?b.hasClass("active")?(f=l.$activeItems.indexOf(b[0]),l.$activeItems.splice(f,1),b.removeClass("active")):l.$activeItems.push(b.addClass("active")[0]):(a(l.$activeItems).removeClass("active"),l.$activeItems=[b.addClass("active")[0]]);l.hideInput(),this.isFocused||l.focus()}},setActiveOption:function(b,c,d){var e,f,g,h,i,j=this;j.$activeOption&&j.$activeOption.removeClass("active"),j.$activeOption=null,b=a(b),b.length&&(j.$activeOption=b.addClass("active"),!c&&y(c)||(e=j.$dropdown_content.height(),f=j.$activeOption.outerHeight(!0),c=j.$dropdown_content.scrollTop()||0,g=j.$activeOption.offset().top-j.$dropdown_content.offset().top+c,h=g,i=g-e+f,g+f>e+c?j.$dropdown_content.stop().animate({scrollTop:i},d?j.settings.scrollDuration:0):g<c&&j.$dropdown_content.stop().animate({scrollTop:h},d?j.settings.scrollDuration:0)))},selectAll:function(){var a=this;"single"!==a.settings.mode&&(a.$activeItems=Array.prototype.slice.apply(a.$control.children(":not(input)").addClass("active")),a.$activeItems.length&&(a.hideInput(),a.close()),a.focus())},hideInput:function(){var a=this;a.setTextboxValue(""),a.$control_input.css({opacity:0,position:"absolute",left:a.rtl?1e4:-1e4}),a.isInputHidden=!0},showInput:function(){this.$control_input.css({opacity:1,position:"relative",left:0}),this.isInputHidden=!1},focus:function(){var a=this;a.isDisabled||(a.ignoreFocus=!0,a.$control_input[0].focus(),window.setTimeout(function(){a.ignoreFocus=!1,a.onFocus()},0))},blur:function(a){this.$control_input[0].blur(),this.onBlur(null,a)},getScoreFunction:function(a){return this.sifter.getScoreFunction(a,this.getSearchOptions())},getSearchOptions:function(){var a=this.settings,b=a.sortField;return"string"==typeof b&&(b=[{field:b}]),{fields:a.searchField,conjunction:a.searchConjunction,sort:b}},search:function(b){var c,d,e,f=this,g=f.settings,h=this.getSearchOptions();if(g.score&&(e=f.settings.score.apply(this,[b]),"function"!=typeof e))throw new Error('Selectize "score" setting must be a function that returns a function');if(b!==f.lastQuery?(f.lastQuery=b,d=f.sifter.search(b,a.extend(h,{score:e})),f.currentResults=d):d=a.extend(!0,{},f.currentResults),g.hideSelected)for(c=d.items.length-1;c>=0;c--)f.items.indexOf(z(d.items[c].id))!==-1&&d.items.splice(c,1);return d},refreshOptions:function(b){var c,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s;"undefined"==typeof b&&(b=!0);var t=this,u=a.trim(t.$control_input.val()),v=t.search(u),w=t.$dropdown_content,x=t.$activeOption&&z(t.$activeOption.attr("data-value"));for(g=v.items.length,"number"==typeof t.settings.maxOptions&&(g=Math.min(g,t.settings.maxOptions)),h={},i=[],c=0;c<g;c++)for(j=t.options[v.items[c].id],k=t.render("option",j),l=j[t.settings.optgroupField]||"",m=a.isArray(l)?l:[l],e=0,f=m&&m.length;e<f;e++)l=m[e],t.optgroups.hasOwnProperty(l)||(l=""),h.hasOwnProperty(l)||(h[l]=document.createDocumentFragment(),i.push(l)),h[l].appendChild(k);for(this.settings.lockOptgroupOrder&&i.sort(function(a,b){var c=t.optgroups[a].$order||0,d=t.optgroups[b].$order||0;return c-d}),n=document.createDocumentFragment(),c=0,g=i.length;c<g;c++)l=i[c],t.optgroups.hasOwnProperty(l)&&h[l].childNodes.length?(o=document.createDocumentFragment(),o.appendChild(t.render("optgroup_header",t.optgroups[l])),o.appendChild(h[l]),n.appendChild(t.render("optgroup",a.extend({},t.optgroups[l],{html:K(o),dom:o})))):n.appendChild(h[l]);if(w.html(n),t.settings.highlight&&v.query.length&&v.tokens.length)for(c=0,g=v.tokens.length;c<g;c++)d(w,v.tokens[c].regex);if(!t.settings.hideSelected)for(c=0,g=t.items.length;c<g;c++)t.getOption(t.items[c]).addClass("selected");p=t.canCreate(u),p&&(w.prepend(t.render("option_create",{input:u})),s=a(w[0].childNodes[0])),t.hasOptions=v.items.length>0||p,t.hasOptions?(v.items.length>0?(r=x&&t.getOption(x),r&&r.length?q=r:"single"===t.settings.mode&&t.items.length&&(q=t.getOption(t.items[0])),q&&q.length||(q=s&&!t.settings.addPrecedence?t.getAdjacentOption(s,1):w.find("[data-selectable]:first"))):q=s,t.setActiveOption(q),b&&!t.isOpen&&t.open()):(t.setActiveOption(null),b&&t.isOpen&&t.close())},addOption:function(b){var c,d,e,f=this;if(a.isArray(b))for(c=0,d=b.length;c<d;c++)f.addOption(b[c]);else(e=f.registerOption(b))&&(f.userOptions[e]=!0,f.lastQuery=null,f.trigger("option_add",e,b))},registerOption:function(a){var b=z(a[this.settings.valueField]);return"undefined"!=typeof b&&null!==b&&!this.options.hasOwnProperty(b)&&(a.$order=a.$order||++this.order,this.options[b]=a,b)},registerOptionGroup:function(a){var b=z(a[this.settings.optgroupValueField]);return!!b&&(a.$order=a.$order||++this.order,this.optgroups[b]=a,b)},addOptionGroup:function(a,b){b[this.settings.optgroupValueField]=a,(a=this.registerOptionGroup(b))&&this.trigger("optgroup_add",a,b)},removeOptionGroup:function(a){this.optgroups.hasOwnProperty(a)&&(delete this.optgroups[a],this.renderCache={},this.trigger("optgroup_remove",a))},clearOptionGroups:function(){this.optgroups={},this.renderCache={},this.trigger("optgroup_clear")},updateOption:function(b,c){var d,e,f,g,h,i,j,k=this;if(b=z(b),f=z(c[k.settings.valueField]),null!==b&&k.options.hasOwnProperty(b)){if("string"!=typeof f)throw new Error("Value must be set in option data");j=k.options[b].$order,f!==b&&(delete k.options[b],g=k.items.indexOf(b),g!==-1&&k.items.splice(g,1,f)),c.$order=c.$order||j,k.options[f]=c,h=k.renderCache.item,i=k.renderCache.option,h&&(delete h[b],delete h[f]),i&&(delete i[b],delete i[f]),k.items.indexOf(f)!==-1&&(d=k.getItem(b),e=a(k.render("item",c)),d.hasClass("active")&&e.addClass("active"),d.replaceWith(e)),k.lastQuery=null,k.isOpen&&k.refreshOptions(!1)}},removeOption:function(a,b){var c=this;a=z(a);var d=c.renderCache.item,e=c.renderCache.option;d&&delete d[a],e&&delete e[a],delete c.userOptions[a],delete c.options[a],c.lastQuery=null,c.trigger("option_remove",a),c.removeItem(a,b)},clearOptions:function(){var a=this;a.loadedSearches={},a.userOptions={},a.renderCache={},a.options=a.sifter.items={},a.lastQuery=null,a.trigger("option_clear"),a.clear()},getOption:function(a){return this.getElementWithValue(a,this.$dropdown_content.find("[data-selectable]"))},getAdjacentOption:function(b,c){var d=this.$dropdown.find("[data-selectable]"),e=d.index(b)+c;return e>=0&&e<d.length?d.eq(e):a()},getElementWithValue:function(b,c){if(b=z(b),"undefined"!=typeof b&&null!==b)for(var d=0,e=c.length;d<e;d++)if(c[d].getAttribute("data-value")===b)return a(c[d]);return a()},getItem:function(a){return this.getElementWithValue(a,this.$control.children())},addItems:function(b,c){for(var d=a.isArray(b)?b:[b],e=0,f=d.length;e<f;e++)this.isPending=e<f-1,this.addItem(d[e],c)},addItem:function(b,c){var d=c?[]:["change"];E(this,d,function(){var d,e,f,g,h,i=this,j=i.settings.mode;return b=z(b),i.items.indexOf(b)!==-1?void("single"===j&&i.close()):void(i.options.hasOwnProperty(b)&&("single"===j&&i.clear(c),"multi"===j&&i.isFull()||(d=a(i.render("item",i.options[b])),h=i.isFull(),i.items.splice(i.caretPos,0,b),i.insertAtCaret(d),(!i.isPending||!h&&i.isFull())&&i.refreshState(),i.isSetup&&(f=i.$dropdown_content.find("[data-selectable]"),i.isPending||(e=i.getOption(b),g=i.getAdjacentOption(e,1).attr("data-value"),i.refreshOptions(i.isFocused&&"single"!==j),g&&i.setActiveOption(i.getOption(g))),!f.length||i.isFull()?i.close():i.positionDropdown(),i.updatePlaceholder(),i.trigger("item_add",b,d),i.updateOriginalInput({silent:c})))))})},removeItem:function(b,c){var d,e,f,g=this;d=b instanceof a?b:g.getItem(b),b=z(d.attr("data-value")),e=g.items.indexOf(b),e!==-1&&(d.remove(),d.hasClass("active")&&(f=g.$activeItems.indexOf(d[0]),g.$activeItems.splice(f,1)),g.items.splice(e,1),g.lastQuery=null,!g.settings.persist&&g.userOptions.hasOwnProperty(b)&&g.removeOption(b,c),e<g.caretPos&&g.setCaret(g.caretPos-1),g.refreshState(),g.updatePlaceholder(),g.updateOriginalInput({silent:c}),g.positionDropdown(),g.trigger("item_remove",b,d))},createItem:function(b,c){var d=this,e=d.caretPos;b=b||a.trim(d.$control_input.val()||"");var f=arguments[arguments.length-1];if("function"!=typeof f&&(f=function(){}),"boolean"!=typeof c&&(c=!0),!d.canCreate(b))return f(),!1;d.lock();var g="function"==typeof d.settings.create?this.settings.create:function(a){var b={};return b[d.settings.labelField]=a,b[d.settings.valueField]=a,b},h=C(function(a){if(d.unlock(),!a||"object"!=typeof a)return f();var b=z(a[d.settings.valueField]);return"string"!=typeof b?f():(d.setTextboxValue(""),d.addOption(a),d.setCaret(e),d.addItem(b),d.refreshOptions(c&&"single"!==d.settings.mode),void f(a))}),i=g.apply(this,[b,h]);return"undefined"!=typeof i&&h(i),!0},refreshItems:function(){this.lastQuery=null,this.isSetup&&this.addItem(this.items),this.refreshState(),this.updateOriginalInput()},refreshState:function(){var a,b=this;b.isRequired&&(b.items.length&&(b.isInvalid=!1),b.$control_input.prop("required",a)),b.refreshClasses()},refreshClasses:function(){var b=this,c=b.isFull(),d=b.isLocked;b.$wrapper.toggleClass("rtl",b.rtl),b.$control.toggleClass("focus",b.isFocused).toggleClass("disabled",b.isDisabled).toggleClass("required",b.isRequired).toggleClass("invalid",b.isInvalid).toggleClass("locked",d).toggleClass("full",c).toggleClass("not-full",!c).toggleClass("input-active",b.isFocused&&!b.isInputHidden).toggleClass("dropdown-active",b.isOpen).toggleClass("has-options",!a.isEmptyObject(b.options)).toggleClass("has-items",b.items.length>0),b.$control_input.data("grow",!c&&!d)},isFull:function(){return null!==this.settings.maxItems&&this.items.length>=this.settings.maxItems},updateOriginalInput:function(a){var b,c,d,e,f=this;if(a=a||{},f.tagType===v){for(d=[],b=0,c=f.items.length;b<c;b++)e=f.options[f.items[b]][f.settings.labelField]||"",d.push('<option value="'+A(f.items[b])+'" selected="selected">'+A(e)+"</option>");d.length||this.$input.attr("multiple")||d.push('<option value="" selected="selected"></option>'),f.$input.html(d.join(""))}else f.$input.val(f.getValue()),f.$input.attr("value",f.$input.val());f.isSetup&&(a.silent||f.trigger("change",f.$input.val()))},updatePlaceholder:function(){if(this.settings.placeholder){var a=this.$control_input;this.items.length?a.removeAttr("placeholder"):a.attr("placeholder",this.settings.placeholder),a.triggerHandler("update",{force:!0})}},open:function(){var a=this;a.isLocked||a.isOpen||"multi"===a.settings.mode&&a.isFull()||(a.focus(),a.isOpen=!0,a.refreshState(),a.$dropdown.css({visibility:"hidden",display:"block"}),a.positionDropdown(),a.$dropdown.css({visibility:"visible"}),a.trigger("dropdown_open",a.$dropdown))},close:function(){var a=this,b=a.isOpen;"single"===a.settings.mode&&a.items.length&&a.hideInput(),a.isOpen=!1,a.$dropdown.hide(),a.setActiveOption(null),a.refreshState(),b&&a.trigger("dropdown_close",a.$dropdown)},positionDropdown:function(){
+var a=this.$control,b="body"===this.settings.dropdownParent?a.offset():a.position();b.top+=a.outerHeight(!0),this.$dropdown.css({width:a.outerWidth(),top:b.top,left:b.left})},clear:function(a){var b=this;b.items.length&&(b.$control.children(":not(input)").remove(),b.items=[],b.lastQuery=null,b.setCaret(0),b.setActiveItem(null),b.updatePlaceholder(),b.updateOriginalInput({silent:a}),b.refreshState(),b.showInput(),b.trigger("clear"))},insertAtCaret:function(b){var c=Math.min(this.caretPos,this.items.length);0===c?this.$control.prepend(b):a(this.$control[0].childNodes[c]).before(b),this.setCaret(c+1)},deleteSelection:function(b){var c,d,e,f,g,h,i,j,k,l=this;if(e=b&&b.keyCode===p?-1:1,f=G(l.$control_input[0]),l.$activeOption&&!l.settings.hideSelected&&(i=l.getAdjacentOption(l.$activeOption,-1).attr("data-value")),g=[],l.$activeItems.length){for(k=l.$control.children(".active:"+(e>0?"last":"first")),h=l.$control.children(":not(input)").index(k),e>0&&h++,c=0,d=l.$activeItems.length;c<d;c++)g.push(a(l.$activeItems[c]).attr("data-value"));b&&(b.preventDefault(),b.stopPropagation())}else(l.isFocused||"single"===l.settings.mode)&&l.items.length&&(e<0&&0===f.start&&0===f.length?g.push(l.items[l.caretPos-1]):e>0&&f.start===l.$control_input.val().length&&g.push(l.items[l.caretPos]));if(!g.length||"function"==typeof l.settings.onDelete&&l.settings.onDelete.apply(l,[g])===!1)return!1;for("undefined"!=typeof h&&l.setCaret(h);g.length;)l.removeItem(g.pop());return l.showInput(),l.positionDropdown(),l.refreshOptions(!0),i&&(j=l.getOption(i),j.length&&l.setActiveOption(j)),!0},advanceSelection:function(a,b){var c,d,e,f,g,h,i=this;0!==a&&(i.rtl&&(a*=-1),c=a>0?"last":"first",d=G(i.$control_input[0]),i.isFocused&&!i.isInputHidden?(f=i.$control_input.val().length,g=a<0?0===d.start&&0===d.length:d.start===f,g&&!f&&i.advanceCaret(a,b)):(h=i.$control.children(".active:"+c),h.length&&(e=i.$control.children(":not(input)").index(h),i.setActiveItem(null),i.setCaret(a>0?e+1:e))))},advanceCaret:function(a,b){var c,d,e=this;0!==a&&(c=a>0?"next":"prev",e.isShiftDown?(d=e.$control_input[c](),d.length&&(e.hideInput(),e.setActiveItem(d),b&&b.preventDefault())):e.setCaret(e.caretPos+a))},setCaret:function(b){var c=this;if(b="single"===c.settings.mode?c.items.length:Math.max(0,Math.min(c.items.length,b)),!c.isPending){var d,e,f,g;for(f=c.$control.children(":not(input)"),d=0,e=f.length;d<e;d++)g=a(f[d]).detach(),d<b?c.$control_input.before(g):c.$control.append(g)}c.caretPos=b},lock:function(){this.close(),this.isLocked=!0,this.refreshState()},unlock:function(){this.isLocked=!1,this.refreshState()},disable:function(){var a=this;a.$input.prop("disabled",!0),a.$control_input.prop("disabled",!0).prop("tabindex",-1),a.isDisabled=!0,a.lock()},enable:function(){var a=this;a.$input.prop("disabled",!1),a.$control_input.prop("disabled",!1).prop("tabindex",a.tabIndex),a.isDisabled=!1,a.unlock()},destroy:function(){var b=this,c=b.eventNS,d=b.revertSettings;b.trigger("destroy"),b.off(),b.$wrapper.remove(),b.$dropdown.remove(),b.$input.html("").append(d.$children).removeAttr("tabindex").removeClass("selectized").attr({tabindex:d.tabindex}).show(),b.$control_input.removeData("grow"),b.$input.removeData("selectize"),a(window).off(c),a(document).off(c),a(document.body).off(c),delete b.$input[0].selectize},render:function(b,c){var d,e,f="",g=!1,h=this;return"option"!==b&&"item"!==b||(d=z(c[h.settings.valueField]),g=!!d),g&&(y(h.renderCache[b])||(h.renderCache[b]={}),h.renderCache[b].hasOwnProperty(d))?h.renderCache[b][d]:(f=a(h.settings.render[b].apply(this,[c,A])),"option"===b||"option_create"===b?f.attr("data-selectable",""):"optgroup"===b&&(e=c[h.settings.optgroupValueField]||"",f.attr("data-group",e)),"option"!==b&&"item"!==b||f.attr("data-value",d||""),g&&(h.renderCache[b][d]=f[0]),f[0])},clearCache:function(a){var b=this;"undefined"==typeof a?b.renderCache={}:delete b.renderCache[a]},canCreate:function(a){var b=this;if(!b.settings.create)return!1;var c=b.settings.createFilter;return a.length&&("function"!=typeof c||c.apply(b,[a]))&&("string"!=typeof c||new RegExp(c).test(a))&&(!(c instanceof RegExp)||c.test(a))}}),L.count=0,L.defaults={options:[],optgroups:[],plugins:[],delimiter:",",splitOn:null,persist:!0,diacritics:!0,create:!1,createOnBlur:!1,createFilter:null,highlight:!0,openOnFocus:!0,maxOptions:1e3,maxItems:null,hideSelected:null,addPrecedence:!1,selectOnTab:!1,preload:!1,allowEmptyOption:!1,closeAfterSelect:!1,scrollDuration:60,loadThrottle:300,loadingClass:"loading",dataAttr:"data-data",optgroupField:"optgroup",valueField:"value",labelField:"text",optgroupLabelField:"label",optgroupValueField:"value",lockOptgroupOrder:!1,sortField:"$order",searchField:["text"],searchConjunction:"and",mode:null,wrapperClass:"selectize-control",inputClass:"selectize-input",dropdownClass:"selectize-dropdown",dropdownContentClass:"selectize-dropdown-content",dropdownParent:null,copyClassesToDropdown:!0,render:{}},a.fn.selectize=function(b){var c=a.fn.selectize.defaults,d=a.extend({},c,b),e=d.dataAttr,f=d.labelField,g=d.valueField,h=d.optgroupField,i=d.optgroupLabelField,j=d.optgroupValueField,k=function(b,c){var h,i,j,k,l=b.attr(e);if(l)for(c.options=JSON.parse(l),h=0,i=c.options.length;h<i;h++)c.items.push(c.options[h][g]);else{var m=a.trim(b.val()||"");if(!d.allowEmptyOption&&!m.length)return;for(j=m.split(d.delimiter),h=0,i=j.length;h<i;h++)k={},k[f]=j[h],k[g]=j[h],c.options.push(k);c.items=j}},l=function(b,c){var k,l,m,n,o=c.options,p={},q=function(a){var b=e&&a.attr(e);return"string"==typeof b&&b.length?JSON.parse(b):null},r=function(b,e){b=a(b);var i=z(b.val());if(i||d.allowEmptyOption)if(p.hasOwnProperty(i)){if(e){var j=p[i][h];j?a.isArray(j)?j.push(e):p[i][h]=[j,e]:p[i][h]=e}}else{var k=q(b)||{};k[f]=k[f]||b.text(),k[g]=k[g]||i,k[h]=k[h]||e,p[i]=k,o.push(k),b.is(":selected")&&c.items.push(i)}},s=function(b){var d,e,f,g,h;for(b=a(b),f=b.attr("label"),f&&(g=q(b)||{},g[i]=f,g[j]=f,c.optgroups.push(g)),h=a("option",b),d=0,e=h.length;d<e;d++)r(h[d],f)};for(c.maxItems=b.attr("multiple")?null:1,n=b.children(),k=0,l=n.length;k<l;k++)m=n[k].tagName.toLowerCase(),"optgroup"===m?s(n[k]):"option"===m&&r(n[k])};return this.each(function(){if(!this.selectize){var e,f=a(this),g=this.tagName.toLowerCase(),h=f.attr("placeholder")||f.attr("data-placeholder");h||d.allowEmptyOption||(h=f.children('option[value=""]').text());var i={placeholder:h,options:[],optgroups:[],items:[]};"select"===g?l(f,i):k(f,i),e=new L(f,a.extend(!0,{},c,i,b))}})},a.fn.selectize.defaults=L.defaults,a.fn.selectize.support={validity:x},L.define("drag_drop",function(b){if(!a.fn.sortable)throw new Error('The "drag_drop" plugin requires jQuery UI "sortable".');if("multi"===this.settings.mode){var c=this;c.lock=function(){var a=c.lock;return function(){var b=c.$control.data("sortable");return b&&b.disable(),a.apply(c,arguments)}}(),c.unlock=function(){var a=c.unlock;return function(){var b=c.$control.data("sortable");return b&&b.enable(),a.apply(c,arguments)}}(),c.setup=function(){var b=c.setup;return function(){b.apply(this,arguments);var d=c.$control.sortable({items:"[data-value]",forcePlaceholderSize:!0,disabled:c.isLocked,start:function(a,b){b.placeholder.css("width",b.helper.css("width")),d.css({overflow:"visible"})},stop:function(){d.css({overflow:"hidden"});var b=c.$activeItems?c.$activeItems.slice():null,e=[];d.children("[data-value]").each(function(){e.push(a(this).attr("data-value"))}),c.setValue(e),c.setActiveItem(b)}})}}()}}),L.define("dropdown_header",function(b){var c=this;b=a.extend({title:"Untitled",headerClass:"selectize-dropdown-header",titleRowClass:"selectize-dropdown-header-title",labelClass:"selectize-dropdown-header-label",closeClass:"selectize-dropdown-header-close",html:function(a){return'<div class="'+a.headerClass+'"><div class="'+a.titleRowClass+'"><span class="'+a.labelClass+'">'+a.title+'</span><a href="javascript:void(0)" class="'+a.closeClass+'">&times;</a></div></div>'}},b),c.setup=function(){var d=c.setup;return function(){d.apply(c,arguments),c.$dropdown_header=a(b.html(b)),c.$dropdown.prepend(c.$dropdown_header)}}()}),L.define("optgroup_columns",function(b){var c=this;b=a.extend({equalizeWidth:!0,equalizeHeight:!0},b),this.getAdjacentOption=function(b,c){var d=b.closest("[data-group]").find("[data-selectable]"),e=d.index(b)+c;return e>=0&&e<d.length?d.eq(e):a()},this.onKeyDown=function(){var a=c.onKeyDown;return function(b){var d,e,f,g;return!this.isOpen||b.keyCode!==j&&b.keyCode!==m?a.apply(this,arguments):(c.ignoreHover=!0,g=this.$activeOption.closest("[data-group]"),d=g.find("[data-selectable]").index(this.$activeOption),g=b.keyCode===j?g.prev("[data-group]"):g.next("[data-group]"),f=g.find("[data-selectable]"),e=f.eq(Math.min(f.length-1,d)),void(e.length&&this.setActiveOption(e)))}}();var d=function(){var a,b=d.width,c=document;return"undefined"==typeof b&&(a=c.createElement("div"),a.innerHTML='<div style="width:50px;height:50px;position:absolute;left:-50px;top:-50px;overflow:auto;"><div style="width:1px;height:100px;"></div></div>',a=a.firstChild,c.body.appendChild(a),b=d.width=a.offsetWidth-a.clientWidth,c.body.removeChild(a)),b},e=function(){var e,f,g,h,i,j,k;if(k=a("[data-group]",c.$dropdown_content),f=k.length,f&&c.$dropdown_content.width()){if(b.equalizeHeight){for(g=0,e=0;e<f;e++)g=Math.max(g,k.eq(e).height());k.css({height:g})}b.equalizeWidth&&(j=c.$dropdown_content.innerWidth()-d(),h=Math.round(j/f),k.css({width:h}),f>1&&(i=j-h*(f-1),k.eq(f-1).css({width:i})))}};(b.equalizeHeight||b.equalizeWidth)&&(B.after(this,"positionDropdown",e),B.after(this,"refreshOptions",e))}),L.define("remove_button",function(b){b=a.extend({label:"&times;",title:"Remove",className:"remove",append:!0},b);var c=function(b,c){c.className="remove-single";var d=b,e='<a href="javascript:void(0)" class="'+c.className+'" tabindex="-1" title="'+A(c.title)+'">'+c.label+"</a>",f=function(a,b){return a+b};b.setup=function(){var g=d.setup;return function(){if(c.append){var h=a(d.$input.context).attr("id"),i=(a("#"+h),d.settings.render.item);d.settings.render.item=function(a){return f(i.apply(b,arguments),e)}}g.apply(b,arguments),b.$control.on("click","."+c.className,function(a){a.preventDefault(),d.isLocked||d.clear()})}}()},d=function(b,c){var d=b,e='<a href="javascript:void(0)" class="'+c.className+'" tabindex="-1" title="'+A(c.title)+'">'+c.label+"</a>",f=function(a,b){var c=a.search(/(<\/[^>]+>\s*)$/);return a.substring(0,c)+b+a.substring(c)};b.setup=function(){var g=d.setup;return function(){if(c.append){var h=d.settings.render.item;d.settings.render.item=function(a){return f(h.apply(b,arguments),e)}}g.apply(b,arguments),b.$control.on("click","."+c.className,function(b){if(b.preventDefault(),!d.isLocked){var c=a(b.currentTarget).parent();d.setActiveItem(c),d.deleteSelection()&&d.setCaret(d.items.length)}})}}()};return"single"===this.settings.mode?void c(this,b):void d(this,b)}),L.define("restore_on_backspace",function(a){var b=this;a.text=a.text||function(a){return a[this.settings.labelField]},this.onKeyDown=function(){var c=b.onKeyDown;return function(b){var d,e;return b.keyCode===p&&""===this.$control_input.val()&&!this.$activeItems.length&&(d=this.caretPos-1,d>=0&&d<this.items.length)?(e=this.options[this.items[d]],this.deleteSelection(b)&&(this.setTextboxValue(a.text.apply(this,[e])),this.refreshOptions(!0)),void b.preventDefault()):c.apply(this,arguments)}}()}),L});
\ No newline at end of file
index fb867833d24ba661773592902858410bd94525f3..dc2f643eae986942ed40273f8516cb3e4f884597 100644 (file)
@@ -4,11 +4,11 @@
 <head>
   <title>{{title}}</title>
   <link rel="stylesheet" href="/static/commitfest/css/jquery-ui.css" type="text/css">
-  <link rel="stylesheet" href="/static/selectable/css/dj.selectable.css" type="text/css" media="all">
   <link rel="stylesheet" href="/static/commitfest/css/bootstrap.css" />
   <link rel="stylesheet" href="/static/commitfest/css/bootstrap-theme.min.css" />
   <link rel="stylesheet" href="/static/commitfest/css/commitfest.css" />
   <link rel="shortcut icon" href="/static/commitfest/favicon.ico" />
+{%block extrahead%}{%endblock%}
 {%if rss_alternate%}  <link rel="alternate" type="application/rss+xml" title="{{rss_alternate_title}}" href="{{rss_alternate}}" />{%endif%}
 </head>
 <body>
@@ -43,7 +43,6 @@
 <script src="/static/commitfest/js/jquery.js"></script>
 <script src="/static/commitfest/js/jquery-ui.js"></script>
 <script src="/static/commitfest/js/bootstrap.js"></script>
-<script src="/static/selectable/js/jquery.dj.selectable.js"></script>
 <script src="/static/commitfest/js/commitfest.js"></script>
 {%block morescript%}{%endblock%}
 </html>
index 6a2adddec99c35a3153178bfbd95811ffcc88611..3f3094b0dde933856493958f395d6541903a8ac6 100644 (file)
@@ -1,5 +1,4 @@
 {%extends "base.html"%}
-{%load selectable_tags%}
 {%load commitfest%}
 
 {%block contents%}
@@ -23,7 +22,7 @@
  <div class="alert alert-danger">{{e}}</div>
    {%endfor%}
   {%endif%}
-{{field|field_class:"form-control"}}
+{%if not field.name in form.selectize_multiple_fields%}{{field|field_class:"form-control"}}{%else%}{{field}}{%endif%}
 {%if field.help_text%}<br/>{{field.help_text|safe}}{%endif%}</div>
  </div>
  {%else%}
 </div>
 {%endblock%}
 
+{%block extrahead%}
+<link rel="stylesheet" href="/static/commitfest/css/selectize.css" />
+<link rel="stylesheet" href="/static/commitfest/css/selectize.default.css" />
+{%endblock%}
 {%block morescript%}
+<script src="/static/commitfest/js/selectize.min.js"></script>
 <script>
-/* Set up djselectable to use bootstrap buttons */
-$.ui.djselectable.prototype._comboButtonTemplate = function (input) {
-    var icon = $("<i>").addClass("glyphicon glyphicon-chevron-down");
-    // Remove current classes on the text input
-    $(input).attr("class", "");
-    // Wrap with input-append
-    $(input).wrap('<div class="input-append" />');
-    // Return button link with the chosen icon
-    return $("<a>").append(icon).addClass("btn btn-default btn-xs");
-};
-$.ui.djselectable.prototype._removeButtonTemplate = function (item) {
-    var icon = $("<i>").addClass("glyphicon glyphicon-remove-sign");
-    // Return button link with the chosen icon
-    return $("<a>").append(icon).addClass("btn btn-default btn-xs selectable-deck-remove");
-};
-
-/* Set up userid selectables to be searchable */
-$('input[data-selectable-url="/selectable/commitfest-userlookup/"]').each(function() {
-   $(this).after(
-         $('<a href="#" class="btn btn-default btn-sm">Import user not listed</a>').click(function () {
-         search_and_store_user();
-
-         return false;
-       })
+/* Set up selectize fields */
+{% for f, url in selectize_multiple_fields %}
+   $('#id_{{f}}').selectize({
+      plugins: ['remove_button'],
+      valueField: 'id',
+      labelField: 'value',
+      searchField: 'value',
+      load: function(query, callback) {
+         if (!query.length) return callback();
+         $.ajax({
+            'url': '{{url}}',
+            'type': 'GET',
+            'dataType': 'json',
+            'data': {
+               'query': query,
+            },
+            'error': function() { callback();},
+            'success': function(res) { callback(res.values);},
+         });
+      }
+   });
+{%endfor%}
+   $('.selectize-control').after(
+      $('<a href="#" class="btn btn-default btn-sm">Import user not listed</a>').click(function () {
+          search_and_store_user();
+      })
    );
-});
 
 $('#searchUserModal').on('shown.bs.modal', function() {
          $('#searchUserSearchField').focus();
index e348692d97111e988db00f828c7629fb6ab781e1..6040c1b06426ae63a0c49823e27fe6b9e16c5f95 100644 (file)
@@ -325,6 +325,7 @@ def patchform(request, cfid, patchid):
         'form': form,
         'patch': patch,
         'title': 'Edit patch',
+        'selectize_multiple_fields': form.selectize_multiple_fields.items(),
         'breadcrumbs': [{'title': cf.title, 'href': '/%s/' % cf.pk},
                         {'title': 'View patch', 'href': '/%s/%s/' % (cf.pk, patch.pk)}],
     })
diff --git a/pgcommitfest/selectable b/pgcommitfest/selectable
deleted file mode 120000 (symlink)
index 81366c5..0000000
+++ /dev/null
@@ -1 +0,0 @@
-../dep/django-selectable/selectable
\ No newline at end of file
index 46c6b985ee9eecab3fcc77518bdecf9a3f24e0ac..4b6ff00ddfd2a02c0850f801d4161e35de9562f7 100644 (file)
@@ -122,7 +122,6 @@ INSTALLED_APPS = (
     'django.contrib.admin',
     # Uncomment the next line to enable admin documentation:
     # 'django.contrib.admindocs',
-    'pgcommitfest.selectable',
     'pgcommitfest.commitfest.apps.CFAppConfig',
     'pgcommitfest.mailqueue',
     'pgcommitfest.userprofile',
index 6cdc21f73e59526f84ffb38de35bc84c83db75f9..9e92961c2096e6cc5fdfc19f5a8c1d091f656d18 100644 (file)
@@ -4,6 +4,7 @@ from django.contrib import admin
 import pgcommitfest.commitfest.views as views
 import pgcommitfest.commitfest.reports as reports
 import pgcommitfest.commitfest.ajax as ajax
+import pgcommitfest.commitfest.lookups as lookups
 import pgcommitfest.auth
 import pgcommitfest.userprofile.views
 
@@ -32,10 +33,9 @@ urlpatterns = [
     url(r'^(\d+)/reports/authorstats/$', reports.authorstats),
     url(r'^search/$', views.global_search),
     url(r'^ajax/(\w+)/$', ajax.main),
+    url(r'^lookups/user/$', lookups.userlookup),
     url(r'^thread_notify/$', views.thread_notify),
 
-    url(r'^selectable/', include('selectable.urls')),
-
     # Auth system integration
     url(r'^(?:account/)?login/?$', pgcommitfest.auth.login),
     url(r'^(?:account/)?logout/?$', pgcommitfest.auth.logout),
diff --git a/selectable b/selectable
deleted file mode 120000 (symlink)
index 3c2cd66..0000000
+++ /dev/null
@@ -1 +0,0 @@
-dep/django-selectable/selectable
\ No newline at end of file