diff options
author | Magnus Hagander | 2021-11-25 12:30:28 +0000 |
---|---|---|
committer | Magnus Hagander | 2021-11-25 12:30:28 +0000 |
commit | 74ff5309290edaaa80dd222a412dfaa7b8947271 (patch) | |
tree | a43b9b5f7d2abeb12caa93c310fd7edb87ab2a4b | |
parent | c4041b26bb73a6269942812caf9621f42292ec37 (diff) |
Replace django-selectable with selectize
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, 795 insertions, 11043 deletions
diff --git a/dep/django-selectable/.coveragerc b/dep/django-selectable/.coveragerc deleted file mode 100644 index 085d731..0000000 --- a/dep/django-selectable/.coveragerc +++ /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 index abd6010..0000000 --- a/dep/django-selectable/.tx/config +++ /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 index ef1920f..0000000 --- a/dep/django-selectable/AUTHORS.txt +++ /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 index 41cda46..0000000 --- a/dep/django-selectable/LICENSE.txt +++ /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 index ce396ae..0000000 --- a/dep/django-selectable/MANIFEST.in +++ /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 index d82053a..0000000 --- a/dep/django-selectable/Makefile +++ /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 index 8a06eb3..0000000 --- a/dep/django-selectable/README.rst +++ /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 index 87770d9..0000000 --- a/dep/django-selectable/docs/Makefile +++ /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 index df64701..0000000 --- a/dep/django-selectable/docs/admin.rst +++ /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 index d3ffe2b..0000000 --- a/dep/django-selectable/docs/advanced.rst +++ /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 index 7343849..0000000 --- a/dep/django-selectable/docs/conf.py +++ /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 index 67bdba5..0000000 --- a/dep/django-selectable/docs/contribute.rst +++ /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 index 60cae8f..0000000 --- a/dep/django-selectable/docs/fields.rst +++ /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 index 6570310..0000000 --- a/dep/django-selectable/docs/index.rst +++ /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 index 00edbae..0000000 --- a/dep/django-selectable/docs/lookups.rst +++ /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 index 253570f..0000000 --- a/dep/django-selectable/docs/make.bat +++ /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 index 6a097a6..0000000 --- a/dep/django-selectable/docs/overview.rst +++ /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 index d5a570d..0000000 --- a/dep/django-selectable/docs/quick-start.rst +++ /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 index c48cedb..0000000 --- a/dep/django-selectable/docs/releases.rst +++ /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 index 80cf04b..0000000 --- a/dep/django-selectable/docs/settings.rst +++ /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 index 234b4ac..0000000 --- a/dep/django-selectable/docs/testing.rst +++ /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 index 2235438..0000000 --- a/dep/django-selectable/docs/widgets.rst +++ /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 index a06c445..0000000 --- a/dep/django-selectable/runtests.py +++ /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 index a93d9cc..0000000 --- a/dep/django-selectable/selectable/__init__.py +++ /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 index 579bf78..0000000 --- a/dep/django-selectable/selectable/apps.py +++ /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 index 9570a19..0000000 --- a/dep/django-selectable/selectable/base.py +++ /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 index c438c85..0000000 --- a/dep/django-selectable/selectable/compat.py +++ /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 index 3245383..0000000 --- a/dep/django-selectable/selectable/decorators.py +++ /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 index 71b39d9..0000000 --- a/dep/django-selectable/selectable/exceptions.py +++ /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 index cc950ad..0000000 --- a/dep/django-selectable/selectable/forms/__init__.py +++ /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 index b2abb0d..0000000 --- a/dep/django-selectable/selectable/forms/base.py +++ /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 index 45e7da1..0000000 --- a/dep/django-selectable/selectable/forms/fields.py +++ /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 index bdc8b01..0000000 --- a/dep/django-selectable/selectable/forms/widgets.py +++ /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 Binary files differdeleted file mode 100644 index 99d42ad..0000000 --- a/dep/django-selectable/selectable/locale/en/LC_MESSAGES/django.mo +++ /dev/null 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 index 0f1f178..0000000 --- a/dep/django-selectable/selectable/locale/en/LC_MESSAGES/django.po +++ /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 Binary files differdeleted file mode 100644 index 96ac6a1..0000000 --- a/dep/django-selectable/selectable/locale/es/LC_MESSAGES/django.mo +++ /dev/null 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 index 6a6bc04..0000000 --- a/dep/django-selectable/selectable/locale/es/LC_MESSAGES/django.po +++ /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 Binary files differdeleted file mode 100644 index 651efbb..0000000 --- a/dep/django-selectable/selectable/locale/fr/LC_MESSAGES/django.mo +++ /dev/null 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 index c1e265f..0000000 --- a/dep/django-selectable/selectable/locale/fr/LC_MESSAGES/django.po +++ /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 Binary files differdeleted file mode 100644 index 8e47890..0000000 --- a/dep/django-selectable/selectable/locale/pl/LC_MESSAGES/django.mo +++ /dev/null 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 index 7ab61a4..0000000 --- a/dep/django-selectable/selectable/locale/pl/LC_MESSAGES/django.po +++ /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 Binary files differdeleted file mode 100644 index 6afef1b..0000000 --- a/dep/django-selectable/selectable/locale/pt_BR/LC_MESSAGES/django.mo +++ /dev/null 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 index 3999877..0000000 --- a/dep/django-selectable/selectable/locale/pt_BR/LC_MESSAGES/django.po +++ /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 Binary files differdeleted file mode 100644 index 7bdf49c..0000000 --- a/dep/django-selectable/selectable/locale/zh_CN/LC_MESSAGES/django.mo +++ /dev/null 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 index ae693db..0000000 --- a/dep/django-selectable/selectable/locale/zh_CN/LC_MESSAGES/django.po +++ /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 index 8dee5f3..0000000 --- a/dep/django-selectable/selectable/models.py +++ /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 index 5a475af..0000000 --- a/dep/django-selectable/selectable/registry.py +++ /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 index 4501ba6..0000000 --- a/dep/django-selectable/selectable/static/selectable/css/dj.selectable.css +++ /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 index 7555a41..0000000 --- a/dep/django-selectable/selectable/static/selectable/js/jquery.dj.selectable.js +++ /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(" ") - .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 index 76aa0c5..0000000 --- a/dep/django-selectable/selectable/templates/selectable/jquery-css.html +++ /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 index 1724365..0000000 --- a/dep/django-selectable/selectable/templates/selectable/jquery-js.html +++ /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 index e69de29..0000000 --- a/dep/django-selectable/selectable/templatetags/__init__.py +++ /dev/null diff --git a/dep/django-selectable/selectable/templatetags/selectable_tags.py b/dep/django-selectable/selectable/templatetags/selectable_tags.py deleted file mode 100755 index c380e53..0000000 --- a/dep/django-selectable/selectable/templatetags/selectable_tags.py +++ /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 index b529c3c..0000000 --- a/dep/django-selectable/selectable/tests/__init__.py +++ /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 index c7e729e..0000000 --- a/dep/django-selectable/selectable/tests/base.py +++ /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 index 7bbb224..0000000 --- a/dep/django-selectable/selectable/tests/qunit/helpers.js +++ /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 index 496a6fc..0000000 --- a/dep/django-selectable/selectable/tests/qunit/index.html +++ /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 index 84ee0a9..0000000 --- a/dep/django-selectable/selectable/tests/qunit/jquery-loader.js +++ /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 index de19ed8..0000000 --- a/dep/django-selectable/selectable/tests/qunit/main.js +++ /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 index 73f1435..0000000 --- a/dep/django-selectable/selectable/tests/qunit/sinon-1.5.2.js +++ /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 index f7b3ffe..0000000 --- a/dep/django-selectable/selectable/tests/qunit/test-events.js +++ /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 index 6720dd4..0000000 --- a/dep/django-selectable/selectable/tests/qunit/test-methods.js +++ /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 index 89c1fff..0000000 --- a/dep/django-selectable/selectable/tests/qunit/test-options.js +++ /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 index b6b2bb9..0000000 --- a/dep/django-selectable/selectable/tests/test_base.py +++ /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 index 2a7ef0e..0000000 --- a/dep/django-selectable/selectable/tests/test_decorators.py +++ /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 index 24d12cb..0000000 --- a/dep/django-selectable/selectable/tests/test_fields.py +++ /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 index ed6bac9..0000000 --- a/dep/django-selectable/selectable/tests/test_forms.py +++ /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 index 082927f..0000000 --- a/dep/django-selectable/selectable/tests/test_functional.py +++ /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 index 74af539..0000000 --- a/dep/django-selectable/selectable/tests/test_templatetags.py +++ /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 index c29ca09..0000000 --- a/dep/django-selectable/selectable/tests/test_views.py +++ /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 index 5209717..0000000 --- a/dep/django-selectable/selectable/tests/test_widgets.py +++ /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 index ed7cf46..0000000 --- a/dep/django-selectable/selectable/tests/urls.py +++ /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 index 83a71d1..0000000 --- a/dep/django-selectable/selectable/tests/views.py +++ /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 index eee1b68..0000000 --- a/dep/django-selectable/selectable/urls.py +++ /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 index 5d8ef62..0000000 --- a/dep/django-selectable/selectable/views.py +++ /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 index d4d5fd8..0000000 --- a/dep/django-selectable/setup.cfg +++ /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 index 40b9ab0..0000000 --- a/dep/django-selectable/setup.py +++ /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 index c6c4e96..0000000 --- a/dep/django-selectable/tox.ini +++ /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 diff --git a/pgcommitfest/commitfest/forms.py b/pgcommitfest/commitfest/forms.py index 4d0430f..1c812f4 100644 --- a/pgcommitfest/commitfest/forms.py +++ b/pgcommitfest/commitfest/forms.py @@ -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) diff --git a/pgcommitfest/commitfest/lookups.py b/pgcommitfest/commitfest/lookups.py index 33e2b4e..18d2ef5 100644 --- a/pgcommitfest/commitfest/lookups.py +++ b/pgcommitfest/commitfest/lookups.py @@ -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') diff --git a/pgcommitfest/commitfest/static/commitfest/css/commitfest.css b/pgcommitfest/commitfest/static/commitfest/css/commitfest.css index e3058a3..0ed8355 100644 --- a/pgcommitfest/commitfest/static/commitfest/css/commitfest.css +++ b/pgcommitfest/commitfest/static/commitfest/css/commitfest.css @@ -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 index 0000000..a763839 --- /dev/null +++ b/pgcommitfest/commitfest/static/commitfest/css/selectize.css @@ -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 index 0000000..4e4a7a1 --- /dev/null +++ b/pgcommitfest/commitfest/static/commitfest/css/selectize.default.css @@ -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 index 0000000..305d531 --- /dev/null +++ b/pgcommitfest/commitfest/static/commitfest/js/selectize.min.js @@ -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,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")},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>…</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+'">×</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:"×",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 diff --git a/pgcommitfest/commitfest/templates/base.html b/pgcommitfest/commitfest/templates/base.html index fb86783..dc2f643 100644 --- a/pgcommitfest/commitfest/templates/base.html +++ b/pgcommitfest/commitfest/templates/base.html @@ -4,11 +4,11 @@ <head> <title>{{title}}</title> <link rel="stylesheet" href="/https/git.postgresql.org/static/commitfest/css/jquery-ui.css" type="text/css"> - <link rel="stylesheet" href="/https/git.postgresql.org/static/selectable/css/dj.selectable.css" type="text/css" media="all"> <link rel="stylesheet" href="/https/git.postgresql.org/static/commitfest/css/bootstrap.css" /> <link rel="stylesheet" href="/https/git.postgresql.org/static/commitfest/css/bootstrap-theme.min.css" /> <link rel="stylesheet" href="/https/git.postgresql.org/static/commitfest/css/commitfest.css" /> <link rel="shortcut icon" href="/https/git.postgresql.org/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="/https/git.postgresql.org/static/commitfest/js/jquery.js"></script> <script src="/https/git.postgresql.org/static/commitfest/js/jquery-ui.js"></script> <script src="/https/git.postgresql.org/static/commitfest/js/bootstrap.js"></script> -<script src="/https/git.postgresql.org/static/selectable/js/jquery.dj.selectable.js"></script> <script src="/https/git.postgresql.org/static/commitfest/js/commitfest.js"></script> {%block morescript%}{%endblock%} </html> diff --git a/pgcommitfest/commitfest/templates/base_form.html b/pgcommitfest/commitfest/templates/base_form.html index 6a2addd..3f3094b 100644 --- a/pgcommitfest/commitfest/templates/base_form.html +++ b/pgcommitfest/commitfest/templates/base_form.html @@ -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%} @@ -69,34 +68,40 @@ </div> {%endblock%} +{%block extrahead%} +<link rel="stylesheet" href="/https/git.postgresql.org/static/commitfest/css/selectize.css" /> +<link rel="stylesheet" href="/https/git.postgresql.org/static/commitfest/css/selectize.default.css" /> +{%endblock%} {%block morescript%} +<script src="/https/git.postgresql.org/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(); diff --git a/pgcommitfest/commitfest/views.py b/pgcommitfest/commitfest/views.py index e348692..6040c1b 100644 --- a/pgcommitfest/commitfest/views.py +++ b/pgcommitfest/commitfest/views.py @@ -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 index 81366c5..0000000 --- a/pgcommitfest/selectable +++ /dev/null @@ -1 +0,0 @@ -../dep/django-selectable/selectable
\ No newline at end of file diff --git a/pgcommitfest/settings.py b/pgcommitfest/settings.py index 46c6b98..4b6ff00 100644 --- a/pgcommitfest/settings.py +++ b/pgcommitfest/settings.py @@ -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', diff --git a/pgcommitfest/urls.py b/pgcommitfest/urls.py index 6cdc21f..9e92961 100644 --- a/pgcommitfest/urls.py +++ b/pgcommitfest/urls.py @@ -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 index 3c2cd66..0000000 --- a/selectable +++ /dev/null @@ -1 +0,0 @@ -dep/django-selectable/selectable
\ No newline at end of file |