Commits

Sławek Ehlert  committed 48082a9

initial commit

  • Participants

Comments (0)

Files changed (17)

+dist/*
+MANIFEST
+[submodule "vendor/select2"]
+	path = vendor/select2
+	url = git://github.com/ivaynberg/select2.git
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   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.
+include setup.py README.rst MANIFEST.in LICENSE VERSION
+recursive-include jquery/static *
+django-staticfiles-select2
+==========================
+Introducing select2 to Django!
+
+
+Usage
+-----
+This application is meant to be used with `django-staticfiles`_.  Make sure
+that staticfiles setup and configured, then install this application using
+`pip`_:
+
+::
+
+	pip install django-staticfiles-select2
+
+Finally, make sure that `select2` is listed in your ``INSTALLED_APPS``.  You
+can use this oneliner to add it as well:
+
+::
+
+	INSTALLED_APPS += ['select2', ]
+
+
+Inspiration
+-----------
+
+Most part of this project are stolen from `django-staticfiles-jquery`_.
+
+.. _django-staticfiles-jquery: https://github.com/tswicegood/django-staticfiles-jquery
+.. _django-staticfiles: https://github.com/jezdez/django-staticfiles
+.. _pip: http://www.pip-installer.org/
+2.0

File select2/__init__.py

Empty file added.

File select2/static/select2/LICENSE

+Copyright 2012 Igor Vaynberg
+ 
+Version: @@ver@@ Timestamp: @@timestamp@@
+
+Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in
+compliance with the License. You may obtain a copy of the License in the LICENSE file, or 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.

File select2/static/select2/select2.css

+/*
+Version: 2.0 Timestamp: Wed, May 16, 2012 10:38:37 AM
+*/
+.select2-container {
+    position: relative;
+    display: inline-block;
+    /* inline-block for ie7 */
+    zoom: 1;
+    *display: inline;
+
+}
+
+.select2-container,
+.select2-drop,
+.select2-search,
+.select2-container .select2-search input{
+  /* 
+    Force border-box so that % widths fit the parent
+    container without overlap because of margin/padding.
+    
+    More Info : http://www.quirksmode.org/css/box.html
+  */
+  -moz-box-sizing: border-box;    /* firefox */
+  -ms-box-sizing: border-box;     /* ie */
+  -webkit-box-sizing: border-box; /* webkit */
+  -khtml-box-sizing: border-box;  /* konqueror */
+  box-sizing: border-box;         /* css3 */
+}
+
+.select2-container .select2-choice {
+    background-color: #fff;
+    background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #eeeeee), color-stop(0.5, white));
+    background-image: -webkit-linear-gradient(center bottom, #eeeeee 0%, white 50%);
+    background-image: -moz-linear-gradient(center bottom, #eeeeee 0%, white 50%);
+    background-image: -o-linear-gradient(bottom, #eeeeee 0%, #ffffff 50%);
+    background-image: -ms-linear-gradient(top, #eeeeee 0%, #ffffff 50%);
+    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr = '#eeeeee', endColorstr = '#ffffff', GradientType = 0);
+    background-image: linear-gradient(top, #eeeeee 0%, #ffffff 50%);
+    -webkit-border-radius: 4px;
+    -moz-border-radius: 4px;
+    border-radius: 4px;
+    -moz-background-clip: padding;
+    -webkit-background-clip: padding-box;
+    background-clip: padding-box;
+    border: 1px solid #aaa;
+    display: block;
+    overflow: hidden;
+    white-space: nowrap;
+    position: relative;
+    height: 26px;
+    line-height: 26px;
+    padding: 0 0 0 8px;
+    color: #444;
+    text-decoration: none;
+}
+
+.select2-container .select2-choice span {
+    margin-right: 26px;
+    display: block;
+    overflow: hidden;
+    white-space: nowrap;
+    -o-text-overflow: ellipsis;
+    -ms-text-overflow: ellipsis;
+    text-overflow: ellipsis;
+}
+
+.select2-container .select2-choice abbr {
+  display: block;
+  position: absolute;
+  right: 26px;
+  top: 8px;
+  width: 12px;
+  height: 12px;
+  font-size: 1px;
+  background: url(select2.png) right top no-repeat;
+  cursor: pointer;
+  text-decoration: none;
+  border:0;
+  outline: 0;
+}
+.select2-container .select2-choice abbr:hover {
+  background-position: right -11px;
+  cursor: pointer;
+}
+
+.select2-container .select2-drop {
+    background: #fff;
+    border: 1px solid #aaa;
+    border-top: 0;
+    position: absolute;
+    top: 100%;
+    -webkit-box-shadow: 0 4px 5px rgba(0, 0, 0, .15);
+    -moz-box-shadow: 0 4px 5px rgba(0, 0, 0, .15);
+    -o-box-shadow: 0 4px 5px rgba(0, 0, 0, .15);
+    box-shadow: 0 4px 5px rgba(0, 0, 0, .15);
+    z-index: 999;
+    width:100%;
+    margin-top:-1px;
+
+  -webkit-border-radius: 0 0 4px 4px;
+  -moz-border-radius: 0 0 4px 4px;
+  border-radius: 0 0 4px 4px;
+}
+
+.select2-container .select2-choice div {
+    -webkit-border-radius: 0 4px 4px 0;
+    -moz-border-radius: 0 4px 4px 0;
+    border-radius: 0 4px 4px 0;
+    -moz-background-clip: padding;
+    -webkit-background-clip: padding-box;
+    background-clip: padding-box;
+    background: #ccc;
+    background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #ccc), color-stop(0.6, #eee));
+    background-image: -webkit-linear-gradient(center bottom, #ccc 0%, #eee 60%);
+    background-image: -moz-linear-gradient(center bottom, #ccc 0%, #eee 60%);
+    background-image: -o-linear-gradient(bottom, #ccc 0%, #eee 60%);
+    background-image: -ms-linear-gradient(top, #cccccc 0%, #eeeeee 60%);
+    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr = '#cccccc', endColorstr = '#eeeeee', GradientType = 0);
+    background-image: linear-gradient(top, #cccccc 0%, #eeeeee 60%);
+    border-left: 1px solid #aaa;
+    position: absolute;
+    right: 0;
+    top: 0;
+    display: block;
+    height: 100%;
+    width: 18px;
+}
+
+.select2-container .select2-choice div b {
+    background: url('select2.png') no-repeat 0 1px;
+    display: block;
+    width: 100%;
+    height: 100%;
+}
+
+.select2-container .select2-search {
+  display: inline-block;
+    white-space: nowrap;
+    z-index: 1010;
+  min-height: 26px;
+  width: 100%;
+  margin: 0;
+  padding-left: 4px;
+  padding-right: 4px;
+}
+
+.select2-container .select2-search input {
+    background: #fff url('select2.png') no-repeat 100% -22px;
+    background: url('select2.png') no-repeat 100% -22px, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee));
+    background: url('select2.png') no-repeat 100% -22px, -webkit-linear-gradient(center bottom, white 85%, #eeeeee 99%);
+    background: url('select2.png') no-repeat 100% -22px, -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%);
+    background: url('select2.png') no-repeat 100% -22px, -o-linear-gradient(bottom, white 85%, #eeeeee 99%);
+    background: url('select2.png') no-repeat 100% -22px, -ms-linear-gradient(top, #ffffff 85%, #eeeeee 99%);
+    background: url('select2.png') no-repeat 100% -22px, linear-gradient(top, #ffffff 85%, #eeeeee 99%);
+    padding: 4px 20px 4px 5px;
+    outline: 0;
+    border: 1px solid #aaa;
+    font-family: sans-serif;
+    font-size: 1em;
+    width:100%;
+    margin:0;
+    height:auto !important;
+    min-height: 26px;
+    -webkit-box-shadow: none;
+    -moz-box-shadow: none;
+    box-shadow: none;
+    border-radius: 0;
+    -moz-border-radius: 0;
+    -webkit-border-radius: 0;  
+}
+
+.select2-container .select2-search input.select2-active {
+    background: #fff url('spinner.gif') no-repeat 100%;
+    background: url('spinner.gif') no-repeat 100%, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee));
+    background: url('spinner.gif') no-repeat 100%, -webkit-linear-gradient(center bottom, white 85%, #eeeeee 99%);
+    background: url('spinner.gif') no-repeat 100%, -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%);
+    background: url('spinner.gif') no-repeat 100%, -o-linear-gradient(bottom, white 85%, #eeeeee 99%);
+    background: url('spinner.gif') no-repeat 100%, -ms-linear-gradient(top, #ffffff 85%, #eeeeee 99%);
+    background: url('spinner.gif') no-repeat 100%, linear-gradient(top, #ffffff 85%, #eeeeee 99%);
+}
+
+
+.select2-container-active .select2-choice,
+.select2-container-active .select2-choices {
+    -webkit-box-shadow: 0 0 5px rgba(0,0,0,.3);
+    -moz-box-shadow   : 0 0 5px rgba(0,0,0,.3);
+    -o-box-shadow     : 0 0 5px rgba(0,0,0,.3);
+    box-shadow        : 0 0 5px rgba(0,0,0,.3);
+    border: 1px solid #5897fb;
+    outline: none;
+}
+
+.select2-dropdown-open .select2-choice {
+  border: 1px solid #aaa;
+  border-bottom-color: transparent;
+  -webkit-box-shadow: 0 1px 0 #fff inset;
+  -moz-box-shadow   : 0 1px 0 #fff inset;
+  -o-box-shadow     : 0 1px 0 #fff inset;
+  box-shadow        : 0 1px 0 #fff inset;
+  background-color: #eee;
+  background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, white), color-stop(0.5, #eeeeee));
+  background-image: -webkit-linear-gradient(center bottom, white 0%, #eeeeee 50%);
+  background-image: -moz-linear-gradient(center bottom, white 0%, #eeeeee 50%);
+  background-image: -o-linear-gradient(bottom, white 0%, #eeeeee 50%);
+  background-image: -ms-linear-gradient(top, #ffffff 0%,#eeeeee 50%);
+  filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#eeeeee',GradientType=0 );
+  background-image: linear-gradient(top, #ffffff 0%,#eeeeee 50%);
+  -webkit-border-bottom-left-radius : 0;
+  -webkit-border-bottom-right-radius: 0;
+  -moz-border-radius-bottomleft : 0;
+  -moz-border-radius-bottomright: 0;
+  border-bottom-left-radius : 0;
+  border-bottom-right-radius: 0;
+}
+
+.select2-dropdown-open .select2-choice div {
+  background: transparent;
+  border-left: none;
+}
+.select2-dropdown-open .select2-choice div b {
+  background-position: -18px 1px;
+}
+
+/* results */
+.select2-container .select2-results {
+  margin: 4px 4px 4px 0;
+  padding: 0 0 0 4px;
+  position: relative;
+  overflow-x: hidden;
+  overflow-y: auto;
+  max-height: 200px;
+}
+.select2-container .select2-results li {
+  line-height: 80%;
+  padding: 7px 7px 8px;
+  margin: 0;
+  list-style: none;
+  cursor: pointer;
+  display: list-item;
+}
+
+.select2-container .select2-results .select2-highlighted {
+  background: #3875d7;
+  color: #fff;
+}
+.select2-container .select2-results li em {
+  background: #feffde;
+  font-style: normal;
+}
+.select2-container .select2-results .select2-highlighted em {
+  background: transparent;
+}
+.select2-container .select2-results .select2-no-results {
+  background: #f4f4f4;
+  display: list-item;
+}
+
+/*
+disabled look for already selected choices in the results dropdown
+.select2-container .select2-results .select2-disabled.select2-highlighted {
+    color: #666;
+    background: #f4f4f4;
+    display: list-item;
+    cursor: default;
+}
+.select2-container .select2-results .select2-disabled {
+  background: #f4f4f4;
+  display: list-item;
+  cursor: default;
+}
+*/
+.select2-container .select2-results .select2-disabled {
+    display: none;
+}
+
+.select2-more-results.select2-active {
+    background: #f4f4f4 url('spinner.gif') no-repeat 100%;
+}
+
+.select2-more-results {
+  background: #f4f4f4;
+  display: list-item;
+}
+
+/* multiselect */
+
+.select2-container-multi .select2-choices {
+    background-color: #fff;
+      background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff));
+      background-image: -webkit-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
+      background-image: -moz-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
+      background-image: -o-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
+      background-image: -ms-linear-gradient(top, #eeeeee 1%, #ffffff 15%);
+      background-image: linear-gradient(top, #eeeeee 1%, #ffffff 15%);
+      border: 1px solid #aaa;
+      margin: 0;
+      padding: 0;
+      cursor: text;
+      overflow: hidden;
+      height: auto !important;
+      height: 1%;
+      position: relative;
+}
+
+.select2-container-multi .select2-drop {
+    margin-top:0;
+}
+
+.select2-container-multi.select2-container-active .select2-choices {
+    -webkit-box-shadow: 0 0 5px rgba(0,0,0,.3);
+    -moz-box-shadow   : 0 0 5px rgba(0,0,0,.3);
+    -o-box-shadow     : 0 0 5px rgba(0,0,0,.3);
+    box-shadow        : 0 0 5px rgba(0,0,0,.3);
+    border: 1px solid #5897fb;
+    outline: none;
+}
+.select2-container-multi .select2-choices li {
+  float: left;
+  list-style: none;
+}
+.select2-container-multi .select2-choices .select2-search-field {
+  white-space: nowrap;
+  margin: 0;
+  padding: 0;
+}
+
+.select2-container-multi .select2-choices .select2-search-field input {
+  color: #666;
+  background: transparent !important;
+  font-family: sans-serif;
+  font-size: 100%;
+  height: 15px;
+  padding: 5px;
+  margin: 1px 0;
+  outline: 0;
+  border: 0;
+  -webkit-box-shadow: none;
+  -moz-box-shadow   : none;
+  -o-box-shadow     : none;
+  box-shadow        : none;
+}
+
+
+.select2-default {
+  color: #999 !important;
+}
+
+.select2-container-multi .select2-choices .select2-search-choice {
+  -webkit-border-radius: 3px;
+  -moz-border-radius   : 3px;
+  border-radius        : 3px;
+  -moz-background-clip   : padding;
+  -webkit-background-clip: padding-box;
+  background-clip        : padding-box;
+  background-color: #e4e4e4;
+  filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f4f4f4', endColorstr='#eeeeee', GradientType=0 );
+  background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), color-stop(100%, #eeeeee));
+  background-image: -webkit-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
+  background-image: -moz-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
+  background-image: -o-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
+  background-image: -ms-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
+  background-image: linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
+  -webkit-box-shadow: 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05);
+  -moz-box-shadow   : 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05);
+  box-shadow        : 0 0 2px #ffffff inset, 0 1px 0 rgba(0,0,0,0.05);
+  color: #333;
+  border: 1px solid #aaaaaa;
+  line-height: 13px;
+  padding: 3px 5px 3px 18px;
+  margin: 3px 0 3px 5px;
+  position: relative;
+  cursor: default;
+}
+.select2-container-multi .select2-choices .select2-search-choice span {
+  cursor: default;
+}
+.select2-container-multi .select2-choices .select2-search-choice-focus {
+  background: #d4d4d4;
+}
+
+.select2-search-choice-close {
+  display: block;
+  position: absolute;
+  right: 3px;
+  top: 4px;
+  width: 12px;
+  height: 13px;
+  font-size: 1px;
+  background: url(select2.png) right top no-repeat;
+  outline: none;
+}
+
+.select2-container-multi .select2-search-choice-close {
+  left: 3px;
+}
+
+
+.select2-container-multi .select2-choices .select2-search-choice .select2-search-choice-close:hover {
+  background-position: right -11px;
+}
+.select2-container-multi .select2-choices .select2-search-choice-focus .select2-search-choice-close {
+  background-position: right -11px;
+}
+
+
+.select2-container-multi .select2-results {
+  margin: -1px 0 0;
+  padding: 0;
+}
+
+/* end multiselect */

File select2/static/select2/select2.js

+/*
+ Copyright 2012 Igor Vaynberg
+ 
+ Version: 2.0 Timestamp: Wed, May 16, 2012 10:38:37 AM
+
+ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in
+ compliance with the License. You may obtain a copy of the License in the LICENSE file, or 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.
+ */
+(function ($, undefined) {
+    "use strict";
+    /*global document, window, jQuery, console */
+
+    if (window.Select2 !== undefined) {
+        return;
+    }
+
+    var KEY, AbstractSelect2, SingleSelect2, MultiSelect2;
+
+    KEY = {
+        TAB: 9,
+        ENTER: 13,
+        ESC: 27,
+        SPACE: 32,
+        LEFT: 37,
+        UP: 38,
+        RIGHT: 39,
+        DOWN: 40,
+        SHIFT: 16,
+        CTRL: 17,
+        ALT: 18,
+        PAGE_UP: 33,
+        PAGE_DOWN: 34,
+        HOME: 36,
+        END: 35,
+        BACKSPACE: 8,
+        DELETE: 46,
+        isArrow: function (k) {
+            k = k.which ? k.which : k;
+            switch (k) {
+            case KEY.LEFT:
+            case KEY.RIGHT:
+            case KEY.UP:
+            case KEY.DOWN:
+                return true;
+            }
+            return false;
+        },
+        isControl: function (k) {
+            k = k.which ? k.which : k;
+            switch (k) {
+            case KEY.SHIFT:
+            case KEY.CTRL:
+            case KEY.ALT:
+                return true;
+            }
+            return false;
+        },
+        isFunctionKey: function (k) {
+            k = k.which ? k.which : k;
+            return k >= 112 && k <= 123;
+        }
+    };
+
+    function indexOf(value, array) {
+        var i = 0, l = array.length, v;
+
+        if (value.constructor === String) {
+            for (; i < l; i = i + 1) if (value.localeCompare(array[i]) === 0) return i;
+        } else {
+            for (; i < l; i = i + 1) {
+                v = array[i];
+                if (v.constructor === String) {
+                    if (v.localeCompare(value) === 0) return i;
+                } else {
+                    if (v === value) return i;
+                }
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * Compares equality of a and b taking into account that a and b may be strings, in which case localCompare is used
+     * @param a
+     * @param b
+     */
+    function equal(a, b) {
+        if (a === b) return true;
+        if (a === undefined || b === undefined) return false;
+        if (a === null || b === null) return false;
+        if (a.constructor === String) return a.localeCompare(b) === 0;
+        if (b.constructor === String) return b.localeCompare(a) === 0;
+        return false;
+    }
+
+    /**
+     * Splits the string into an array of values, trimming each value. An empty array is returned for nulls or empty
+     * strings
+     * @param string
+     * @param separator
+     */
+    function splitVal(string, separator) {
+        var val, i, l;
+        if (string === null || string.length < 1) return [];
+        val = string.split(separator);
+        for (i = 0, l = val.length; i < l; i = i + 1) val[i] = $.trim(val[i]);
+        return val;
+    }
+
+    function getSideBorderPadding(element) {
+        return element.outerWidth() - element.width();
+    }
+
+    function installKeyUpChangeEvent(element) {
+        element.bind("keydown", function () {
+            element.data("keyup-change-value", element.val());
+        });
+        element.bind("keyup", function () {
+            if (element.val() !== element.data("keyup-change-value")) {
+                element.trigger("keyup-change");
+            }
+        });
+    }
+
+    /**
+     * filters mouse events so an event is fired only if the mouse moved.
+     *
+     * filters out mouse events that occur when mouse is stationary but
+     * the elements under the pointer are scrolled.
+     */
+    $(document).delegate("*", "mousemove", function (e) {
+        $(document).data("select2-lastpos", {x: e.pageX, y: e.pageY});
+    });
+    function installFilteredMouseMove(element) {
+        element.bind("mousemove", function (e) {
+            var lastpos = $(document).data("select2-lastpos");
+            if (lastpos === undefined || lastpos.x !== e.pageX || lastpos.y !== e.pageY) {
+                $(e.target).trigger("mousemove-filtered", e);
+            }
+        });
+    }
+
+    /**
+     * Debounces a function. Returns a function that calls the original fn function only if no invocations have been made
+     * within the last quietMillis milliseconds.
+     *
+     * @param quietMillis number of milliseconds to wait before invoking fn
+     * @param fn function to be debounced
+     * @return debounced version of fn
+     */
+    function debounce(quietMillis, fn) {
+        var timeout;
+        return function () {
+            window.clearTimeout(timeout);
+            timeout = window.setTimeout(fn, quietMillis);
+        };
+    }
+
+    function installDebouncedScroll(threshold, element) {
+        var notify = debounce(threshold, function (e) { element.trigger("scroll-debounced", e);});
+        element.bind("scroll", function (e) {
+            if (indexOf(e.target, element.get()) >= 0) notify(e);
+        });
+    }
+
+    function killEvent(event) {
+        event.preventDefault();
+        event.stopPropagation();
+    }
+
+    function measureTextWidth(e) {
+        var sizer, width;
+        sizer = $("<div></div>").css({
+            position: "absolute",
+            left: "-1000px",
+            top: "-1000px",
+            display: "none",
+            fontSize: e.css("fontSize"),
+            fontFamily: e.css("fontFamily"),
+            fontStyle: e.css("fontStyle"),
+            fontWeight: e.css("fontWeight"),
+            letterSpacing: e.css("letterSpacing"),
+            textTransform: e.css("textTransform"),
+            whiteSpace: "nowrap"
+        });
+        sizer.text(e.val());
+        $("body").append(sizer);
+        width = sizer.width();
+        sizer.remove();
+        return width;
+    }
+
+    /**
+     * Produces an ajax-based query function
+     *
+     * @param options object containing configuration paramters
+     * @param options.transport function that will be used to execute the ajax request. must be compatible with parameters supported by $.ajax
+     * @param options.url url for the data
+     * @param options.data a function(searchTerm, pageNumber) that should return an object containing query string parameters for the above url.
+     * @param options.dataType request data type: ajax, jsonp, other datatatypes supported by jQuery's $.ajax function or the transport function if specified
+     * @param options.quietMillis (optional) milliseconds to wait before making the ajaxRequest, helps debounce the ajax function if invoked too often
+     * @param options.results a function(remoteData, pageNumber) that converts data returned form the remote request to the format expected by Select2.
+     *      The expected format is an object containing the following keys:
+     *      results array of objects that will be used as choices
+     *      more (optional) boolean indicating whether there are more results available
+     *      Example: {results:[{id:1, text:'Red'},{id:2, text:'Blue'}], more:true}
+     */
+    function ajax(options) {
+        var timeout, // current scheduled but not yet executed request
+            requestSequence = 0, // sequence used to drop out-of-order responses
+            handler = null,
+            quietMillis = options.quietMillis || 100;
+
+        return function (query) {
+            window.clearTimeout(timeout);
+            timeout = window.setTimeout(function () {
+                requestSequence += 1; // increment the sequence
+                var requestNumber = requestSequence, // this request's sequence number
+                    data = options.data, // ajax data function
+                    transport = options.transport || $.ajax;
+
+                data = data.call(this, query.term, query.page);
+
+                if( null !== handler){
+                    handler.abort();
+                }
+                handler = transport.call(null, {
+                    url: options.url,
+                    dataType: options.dataType,
+                    data: data,
+                    success: function (data) {
+                        if (requestNumber < requestSequence) {
+                            return;
+                        }
+                        // TODO 3.0 - replace query.page with query so users have access to term, page, etc.
+                        query.callback(options.results(data, query.page));
+                    }
+                });
+            }, quietMillis);
+        };
+    }
+
+    /**
+     * Produces a query function that works with a local array
+     *
+     * @param options object containing configuration parameters. The options parameter can either be an array or an
+     * object.
+     *
+     * If the array form is used it is assumed that it contains objects with 'id' and 'text' keys.
+     *
+     * If the object form is used ti is assumed that it contains 'data' and 'text' keys. The 'data' key should contain
+     * an array of objects that will be used as choices. These objects must contain at least an 'id' key. The 'text'
+     * key can either be a String in which case it is expected that each element in the 'data' array has a key with the
+     * value of 'text' which will be used to match choices. Alternatively, text can be a function(item) that can extract
+     * the text.
+     */
+    function local(options) {
+        var data = options, // data elements
+            text = function (item) { return item.text; }; // function used to retrieve the text portion of a data item that is matched against the search
+
+        if (!$.isArray(data)) {
+            text = data.text;
+            // if text is not a function we assume it to be a key name
+            if (!$.isFunction(text)) text = function (item) { return item[data.text]; };
+            data = data.results;
+        }
+
+        return function (query) {
+            var t = query.term.toUpperCase(), filtered = {};
+            if (t === "") {
+                query.callback({results: data});
+                return;
+            }
+            filtered.results = $(data)
+                .filter(function () {return text(this).toUpperCase().indexOf(t) >= 0;})
+                .get();
+            query.callback(filtered);
+        };
+    }
+
+    // TODO javadoc
+    function tags(data) {
+        // TODO even for a function we should probably return a wrapper that does the same object/string check as
+        // the function for arrays. otherwise only functions that return objects are supported.
+        if ($.isFunction(data)) {
+            return data;
+        }
+
+        // if not a function we assume it to be an array
+
+        return function (query) {
+            var t = query.term.toUpperCase(), filtered = {results: []};
+            $(data).each(function () {
+                var isObject = this.text !== undefined,
+                    text = isObject ? this.text : this;
+                if (t === "" || text.toUpperCase().indexOf(t) >= 0) {
+                    filtered.results.push(isObject ? this : {id: this, text: this});
+                }
+            });
+            query.callback(filtered);
+        };
+    }
+
+    /**
+     * blurs any Select2 container that has focus when an element outside them was clicked or received focus
+     */
+    $(document).ready(function () {
+        $(document).delegate("*", "mousedown focusin", function (e) {
+            var target = $(e.target).closest("div.select2-container").get(0);
+            $(document).find("div.select2-container-active").each(function () {
+                if (this !== target) $(this).data("select2").blur();
+            });
+        });
+    });
+
+    /**
+     * Creates a new class
+     *
+     * @param superClass
+     * @param methods
+     */
+    function clazz(SuperClass, methods) {
+        var constructor = function () {};
+        constructor.prototype = new SuperClass;
+        constructor.prototype.constructor = constructor;
+        constructor.prototype.parent = SuperClass.prototype;
+        constructor.prototype = $.extend(constructor.prototype, methods);
+        return constructor;
+    }
+
+    AbstractSelect2 = clazz(Object, {
+
+        bind: function (func) {
+            var self = this;
+            return function () {
+                func.apply(self, arguments);
+            };
+        },
+
+        init: function (opts) {
+            var results, search, resultsSelector = ".select2-results";
+
+            // prepare options
+            this.opts = opts = this.prepareOpts(opts);
+
+            this.id=opts.id;
+
+            // destroy if called on an existing component
+            if (opts.element.data("select2") !== undefined) {
+                this.destroy();
+            }
+
+            this.container = this.createContainer();
+
+            if (opts.element.attr("class") !== undefined) {
+                this.container.addClass(opts.element.attr("class"));
+            }
+
+            // swap container for the element
+            this.opts.element
+                .data("select2", this)
+                .hide()
+                .after(this.container);
+            this.container.data("select2", this);
+
+            this.dropdown = this.container.find(".select2-drop");
+            this.results = results = this.container.find(resultsSelector);
+            this.search = search = this.container.find("input[type=text]");
+
+            this.resultsPage = 0;
+
+            // initialize the container
+            this.initContainer();
+
+            installFilteredMouseMove(this.results);
+            this.container.delegate(resultsSelector, "mousemove-filtered", this.bind(this.highlightUnderEvent));
+
+            installDebouncedScroll(80, this.results);
+            this.container.delegate(resultsSelector, "scroll-debounced", this.bind(this.loadMoreIfNeeded));
+
+            // if jquery.mousewheel plugin is installed we can prevent out-of-bounds scrolling of results via mousewheel
+            if ($.fn.mousewheel) {
+                results.mousewheel(function (e, delta, deltaX, deltaY) {
+                    var top = results.scrollTop(), height;
+                    if (deltaY > 0 && top - deltaY <= 0) {
+                        results.scrollTop(0);
+                        killEvent(e);
+                    } else if (deltaY < 0 && results.get(0).scrollHeight - results.scrollTop() + deltaY <= results.height()) {
+                        results.scrollTop(results.get(0).scrollHeight - results.height());
+                        killEvent(e);
+                    }
+                });
+            }
+
+            installKeyUpChangeEvent(search);
+            search.bind("keyup-change", this.bind(this.updateResults));
+            search.bind("focus", function () { search.addClass("select2-focused");});
+            search.bind("blur", function () { search.removeClass("select2-focused");});
+
+            this.container.delegate(resultsSelector, "click", this.bind(function (e) {
+                if ($(e.target).closest(".select2-result:not(.select2-disabled)").length > 0) {
+                    this.highlightUnderEvent(e);
+                    this.selectHighlighted(e);
+                } else {
+                    killEvent(e);
+                    this.focusSearch();
+                }
+            }));
+
+            if ($.isFunction(this.opts.initSelection)) {
+                // initialize selection based on the current value of the source element
+                this.initSelection();
+
+                // if the user has provided a function that can set selection based on the value of the source element
+                // we monitor the change event on the element and trigger it, allowing for two way synchronization
+                this.monitorSource();
+            }
+        },
+
+        destroy: function () {
+            var select2 = this.opts.element.data("select2");
+            if (select2 !== undefined) {
+                select2.container.remove();
+                select2.opts.element
+                    .removeData("select2")
+                    .unbind(".select2")
+                    .show();
+            }
+        },
+
+        prepareOpts: function (opts) {
+            var element, select, idKey;
+
+            element = opts.element;
+
+            if (element.get(0).tagName.toLowerCase() === "select") {
+                this.select = select = opts.element;
+            }
+
+            if (select) {
+                // these options are not allowed when attached to a select because they are picked up off the element itself
+                $.each(["id", "multiple", "ajax", "query", "createSearchChoice", "initSelection", "data", "tags"], function () {
+                    if (this in opts) {
+                        throw new Error("Option '" + this + "' is not allowed for Select2 when attached to a <select> element.");
+                    }
+                });
+            }
+
+            opts = $.extend({}, {
+                formatResult: function (data) { return data.text; },
+                formatSelection: function (data) { return data.text; },
+                formatNoMatches: function () { return "No matches found"; },
+                formatInputTooShort: function (input, min) { return "Please enter " + (min - input.length) + " more characters"; },
+                minimumResultsForSearch: 0,
+                minimumInputLength: 0,
+                id: function (e) { return e.id; }
+            }, opts);
+
+            if (typeof(opts.id) !== "function") {
+                idKey = opts.id;
+                opts.id = function (e) { return e[idKey]; };
+            }
+
+            if (select) {
+                opts.query = this.bind(function (query) {
+                    var data = {results: [], more: false},
+                        term = query.term.toUpperCase(),
+                        placeholder = this.getPlaceholder();
+                    element.find("option").each(function (i) {
+                        var e = $(this),
+                            text = e.text();
+
+                        if (i === 0 && placeholder !== undefined && text === "") return true;
+
+                        if (text.toUpperCase().indexOf(term) >= 0) {
+                            data.results.push({id: e.attr("value"), text: text});
+                        }
+                    });
+                    query.callback(data);
+                });
+                // this is needed because inside val() we construct choices from options and there id is hardcoded
+                opts.id=function(e) { return e.id; };
+            } else {
+                if (!("query" in opts)) {
+                    if ("ajax" in opts) {
+                        opts.query = ajax(opts.ajax);
+                    } else if ("data" in opts) {
+                        opts.query = local(opts.data);
+                    } else if ("tags" in opts) {
+                        opts.query = tags(opts.tags);
+                        opts.createSearchChoice = function (term) { return {id: term, text: term}; };
+                        opts.initSelection = function (element) {
+                            var data = [];
+                            $(splitVal(element.val(), ",")).each(function () {
+                                data.push({id: this, text: this});
+                            });
+                            return data;
+                        };
+                    }
+                }
+            }
+            if (typeof(opts.query) !== "function") {
+                throw "query function not defined for Select2 " + opts.element.attr("id");
+            }
+
+            return opts;
+        },
+
+        /**
+         * Monitor the original element for changes and update select2 accordingly
+         */
+        monitorSource: function () {
+            this.opts.element.bind("change.select2", this.bind(function (e) {
+                if (this.opts.element.data("select2-change-triggered") !== true) {
+                    this.initSelection();
+                }
+            }));
+        },
+
+        /**
+         * Triggers the change event on the source element
+         */
+        triggerChange: function () {
+            // Prevents recursive triggering
+            this.opts.element.data("select2-change-triggered", true);
+            this.opts.element.trigger("change");
+            this.opts.element.data("select2-change-triggered", false);
+        },
+
+        opened: function () {
+            return this.container.hasClass("select2-dropdown-open");
+        },
+
+        open: function () {
+            if (this.opened()) return;
+
+            this.container.addClass("select2-dropdown-open").addClass("select2-container-active");
+
+            this.updateResults(true);
+            this.dropdown.show();
+            this.focusSearch();
+        },
+
+        close: function () {
+            if (!this.opened()) return;
+
+            this.dropdown.hide();
+            this.container.removeClass("select2-dropdown-open");
+            this.results.empty();
+            this.clearSearch();
+        },
+
+        clearSearch: function () {
+
+        },
+
+        ensureHighlightVisible: function () {
+            var results = this.results, children, index, child, hb, rb, y, more;
+
+            children = results.children(".select2-result");
+            index = this.highlight();
+
+            if (index < 0) return;
+
+            child = $(children[index]);
+
+            hb = child.offset().top + child.outerHeight();
+
+            // if this is the last child lets also make sure select2-more-results is visible
+            if (index === children.length - 1) {
+                more = results.find("li.select2-more-results");
+                if (more.length > 0) {
+                    hb = more.offset().top + more.outerHeight();
+                }
+            }
+
+            rb = results.offset().top + results.outerHeight();
+            if (hb > rb) {
+                results.scrollTop(results.scrollTop() + (hb - rb));
+            }
+            y = child.offset().top - results.offset().top;
+
+            // make sure the top of the element is visible
+            if (y < 0) {
+                results.scrollTop(results.scrollTop() + y); // y is negative
+            }
+        },
+
+        moveHighlight: function (delta) {
+            var choices = this.results.children(".select2-result"),
+                index = this.highlight();
+
+            while (index > -1 && index < choices.length) {
+                index += delta;
+                if (!$(choices[index]).hasClass("select2-disabled")) {
+                    this.highlight(index);
+                    break;
+                }
+            }
+        },
+
+        highlight: function (index) {
+            var choices = this.results.children(".select2-result");
+
+            if (arguments.length === 0) {
+                return indexOf(choices.filter(".select2-highlighted")[0], choices.get());
+            }
+
+            choices.removeClass("select2-highlighted");
+
+            if (index >= choices.length) index = choices.length - 1;
+            if (index < 0) index = 0;
+
+            $(choices[index]).addClass("select2-highlighted");
+            this.ensureHighlightVisible();
+
+            if (this.opened()) this.focusSearch();
+        },
+
+        highlightUnderEvent: function (event) {
+            var el = $(event.target).closest(".select2-result");
+            if (el.length > 0) {
+                this.highlight(el.index());
+            }
+        },
+
+        loadMoreIfNeeded: function () {
+            var results = this.results,
+                more = results.find("li.select2-more-results"),
+                below, // pixels the element is below the scroll fold, below==0 is when the element is starting to be visible
+                offset = -1, // index of first element without data
+                page = this.resultsPage + 1;
+
+            if (more.length === 0) return;
+
+            below = more.offset().top - results.offset().top - results.height();
+
+            if (below <= 0) {
+                more.addClass("select2-active");
+                this.opts.query({term: this.search.val(), page: page, callback: this.bind(function (data) {
+                    var parts = [], self = this;
+                    $(data.results).each(function () {
+                        parts.push("<li class='select2-result'>");
+                        parts.push(self.opts.formatResult(this));
+                        parts.push("</li>");
+                    });
+                    more.before(parts.join(""));
+                    results.find(".select2-result").each(function (i) {
+                        var e = $(this);
+                        if (e.data("select2-data") !== undefined) {
+                            offset = i;
+                        } else {
+                            e.data("select2-data", data.results[i - offset - 1]);
+                        }
+                    });
+                    if (data.more) {
+                        more.removeClass("select2-active");
+                    } else {
+                        more.remove();
+                    }
+                    this.resultsPage = page;
+                })});
+            }
+        },
+
+        /**
+         * @param initial whether or not this is the call to this method right after the dropdown has been opened
+         */
+        updateResults: function (initial) {
+            var search = this.search, results = this.results, opts = this.opts, self=this;
+
+            search.addClass("select2-active");
+
+            function render(html) {
+                results.html(html);
+                results.scrollTop(0);
+                search.removeClass("select2-active");
+            }
+
+            if (search.val().length < opts.minimumInputLength) {
+                render("<li class='select2-no-results'>" + opts.formatInputTooShort(search.val(), opts.minimumInputLength) + "</li>");
+                return;
+            }
+
+            this.resultsPage = 1;
+            opts.query({term: search.val(), page: this.resultsPage, callback: this.bind(function (data) {
+                var parts = [], // html parts
+                    def; // default choice
+
+                // create a default choice and prepend it to the list
+                if (this.opts.createSearchChoice && search.val() !== "") {
+                    def = this.opts.createSearchChoice.call(null, search.val(), data.results);
+                    if (def !== undefined && def !== null && self.id(def) !== undefined && self.id(def) !== null) {
+                        if ($(data.results).filter(
+                            function () {
+                                return equal(self.id(this), self.id(def));
+                            }).length === 0) {
+                            data.results.unshift(def);
+                        }
+                    }
+                }
+
+                if (data.results.length === 0) {
+                    render("<li class='select2-no-results'>" + opts.formatNoMatches(search.val()) + "</li>");
+                    return;
+                }
+
+                $(data.results).each(function () {
+                    parts.push("<li class='select2-result'>");
+                    parts.push(opts.formatResult(this));
+                    parts.push("</li>");
+                });
+
+                if (data.more === true) {
+                    parts.push("<li class='select2-more-results'>Loading more results...</li>");
+                }
+
+                render(parts.join(""));
+                results.children(".select2-result").each(function (i) {
+                    var d = data.results[i];
+                    $(this).data("select2-data", d);
+                });
+                this.postprocessResults(data, initial);
+            })});
+        },
+
+        cancel: function () {
+            this.close();
+        },
+
+        blur: function () {
+            /* we do this in a timeout so that current event processing can complete before this code is executed.
+             this allows tab index to be preserved even if this code blurs the textfield */
+            window.setTimeout(this.bind(function () {
+                this.close();
+                this.container.removeClass("select2-container-active");
+                this.clearSearch();
+                this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus");
+                this.search.blur();
+            }), 10);
+        },
+
+        focusSearch: function () {
+            /* we do this in a timeout so that current event processing can complete before this code is executed.
+             this makes sure the search field is focussed even if the current event would blur it */
+            window.setTimeout(this.bind(function () {
+                this.search.focus();
+            }), 10);
+        },
+
+        selectHighlighted: function () {
+            var data = this.results.find(".select2-highlighted:not(.select2-disabled)").data("select2-data");
+            if (data) {
+                this.onSelect(data);
+            }
+        },
+
+        getPlaceholder: function () {
+            return this.opts.element.attr("placeholder") || this.opts.element.data("placeholder") || this.opts.placeholder;
+        },
+
+        /**
+         * Get the desired width for the container element.  This is
+         * derived first from option `width` passed to select2, then
+         * the inline 'style' on the original element, and finally
+         * falls back to the jQuery calculated element width.
+         *
+         * @returns The width string (with units) for the container.
+         */
+        getContainerWidth: function () {
+            var style, attrs, matches, i, l;
+            if (this.opts.width !== undefined)
+                return this.opts.width;
+
+            style = this.opts.element.attr('style');
+            if (style !== undefined) {
+                attrs = style.split(';');
+                for (i = 0, l = attrs.length; i < l; i = i + 1) {
+                    matches = attrs[i].replace(/\s/g, '')
+                        .match(/width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/);
+                    if (matches !== null && matches.length >= 1)
+                        return matches[1];
+                }
+            }
+            return this.opts.element.width() + 'px';
+        }
+    });
+
+    SingleSelect2 = clazz(AbstractSelect2, {
+
+        createContainer: function () {
+            return $("<div></div>", {
+                "class": "select2-container",
+                "style": "width: " + this.getContainerWidth()
+            }).html([
+                "    <a href='javascript:void(0)' class='select2-choice'>",
+                "   <span></span><abbr class='select2-search-choice-close' style='display:none;'></abbr>",
+                "   <div><b></b></div>" ,
+                "</a>",
+                "    <div class='select2-drop' style='display:none;'>" ,
+                "   <div class='select2-search'>" ,
+                "       <input type='text' autocomplete='off'/>" ,
+                "   </div>" ,
+                "   <ul class='select2-results'>" ,
+                "   </ul>" ,
+                "</div>"].join(""));
+        },
+
+        open: function () {
+
+            if (this.opened()) return;
+
+            this.parent.open.apply(this, arguments);
+
+        },
+
+        close: function () {
+            if (!this.opened()) return;
+            this.parent.close.apply(this, arguments);
+        },
+
+        focus: function () {
+            this.close();
+            this.selection.focus();
+        },
+
+        isFocused: function () {
+            return this.selection.is(":focus");
+        },
+
+        cancel: function () {
+            this.parent.cancel.apply(this, arguments);
+            this.selection.focus();
+        },
+
+        initContainer: function () {
+
+            var selection, container = this.container, clickingInside = false,
+                selector = ".select2-choice";
+
+            this.selection = selection = container.find(selector);
+
+            this.search.bind("keydown", this.bind(function (e) {
+                switch (e.which) {
+                case KEY.UP:
+                case KEY.DOWN:
+                    this.moveHighlight((e.which === KEY.UP) ? -1 : 1);
+                    killEvent(e);
+                    return;
+                case KEY.TAB:
+                case KEY.ENTER:
+                    this.selectHighlighted();
+                    killEvent(e);
+                    return;
+                case KEY.ESC:
+                    this.cancel(e);
+                    e.preventDefault();
+                    return;
+                }
+            }));
+
+            container.delegate(selector, "click", this.bind(function (e) {
+                clickingInside = true;
+
+                if (this.opened()) {
+                    this.close();
+                    selection.focus();
+                } else {
+                    this.open();
+                }
+                e.preventDefault();
+
+                clickingInside = false;
+            }));
+            container.delegate(selector, "keydown", this.bind(function (e) {
+                if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.ESC) {
+                    return;
+                }
+                this.open();
+                if (e.which === KEY.PAGE_UP || e.which === KEY.PAGE_DOWN || e.which === KEY.SPACE) {
+                    // prevent the page from scrolling
+                    killEvent(e);
+                }
+                if (e.which === KEY.ENTER) {
+                    // do not propagate the event otherwise we open, and propagate enter which closes
+                    killEvent(e);
+                }
+            }));
+            container.delegate(selector, "focus", function () { container.addClass("select2-container-active"); });
+            container.delegate(selector, "blur", this.bind(function () {
+                if (clickingInside) return;
+                if (!this.opened()) this.blur();
+            }));
+
+            selection.delegate("abbr", "click", this.bind(function (e) {
+                this.val("");
+                killEvent(e);
+                this.close();
+                this.triggerChange();
+            }));
+
+            this.setPlaceholder();
+        },
+
+        /**
+         * Sets selection based on source element's value
+         */
+        initSelection: function () {
+            var selected;
+            if (this.opts.element.val() === "") {
+                this.updateSelection({id: "", text: ""});
+            } else {
+                selected = this.opts.initSelection.call(null, this.opts.element);
+                if (selected !== undefined && selected !== null) {
+                    this.updateSelection(selected);
+                }
+            }
+
+            this.close();
+            this.setPlaceholder();
+        },
+
+        prepareOpts: function () {
+            var opts = this.parent.prepareOpts.apply(this, arguments);
+
+            if (opts.element.get(0).tagName.toLowerCase() === "select") {
+                // install sthe selection initializer
+                opts.initSelection = function (element) {
+                    var selected = element.find(":selected");
+                    // a single select box always has a value, no need to null check 'selected'
+                    return {id: selected.attr("value"), text: selected.text()};
+                };
+            }
+
+            return opts;
+        },
+
+        setPlaceholder: function () {
+            var placeholder = this.getPlaceholder();
+
+            if (this.opts.element.val() === "" && placeholder !== undefined) {
+
+                // check for a first blank option if attached to a select
+                if (this.select && this.select.find("option:first").text() !== "") return;
+
+                if (typeof(placeholder) === "object") {
+                    this.updateSelection(placeholder);
+                } else {
+                    this.selection.find("span").html(placeholder);
+                }
+                this.selection.addClass("select2-default");
+
+                this.selection.find("abbr").hide();
+            }
+        },
+
+        postprocessResults: function (data, initial) {
+            var selected = 0, self = this, showSearchInput = true;
+
+            // find the selected element in the result list
+
+            this.results.find(".select2-result").each(function (i) {
+                if (equal(self.id($(this).data("select2-data")), self.opts.element.val())) {
+                    selected = i;
+                    return false;
+                }
+            });
+
+            // and highlight it
+
+            this.highlight(selected);
+
+            // hide the search box if this is the first we got the results and there are a few of them
+
+            if (initial === true) {
+                showSearchInput = data.results.length >= this.opts.minimumResultsForSearch;
+                this.search.parent().toggle(showSearchInput);
+
+                //add "select2-with-searchbox" to the container if search box is shown
+                this.container[showSearchInput ? "addClass" : "removeClass"]("select2-with-searchbox");
+            }
+
+        },
+
+        onSelect: function (data) {
+            var old = this.opts.element.val();
+
+            this.opts.element.val(this.id(data));
+            this.updateSelection(data);
+            this.close();
+            this.selection.focus();
+
+            if (!equal(old, this.id(data))) { this.triggerChange(); }
+        },
+
+        updateSelection: function (data) {
+            this.selection
+                .find("span")
+                .html(this.opts.formatSelection(data));
+
+            this.selection.removeClass("select2-default");
+
+            if (this.opts.allowClear && this.getPlaceholder() !== undefined) {
+                this.selection.find("abbr").show();
+            }
+        },
+
+        val: function () {
+            var val, data = null;
+
+            if (arguments.length === 0) {
+                return this.opts.element.val();
+            }
+
+            val = arguments[0];
+
+            if (this.select) {
+                // val is an id
+                this.select
+                    .val(val)
+                    .find(":selected").each(function () {
+                        data = {id: $(this).attr("value"), text: $(this).text()};
+                        return false;
+                    });
+                this.updateSelection(data);
+            } else {
+                // val is an object. !val is true for [undefined,null,'']
+                this.opts.element.val(!val ? "" : this.id(val));
+                this.updateSelection(val);
+            }
+            this.setPlaceholder();
+
+        },
+
+        clearSearch: function () {
+            this.search.val("");
+        }
+    });
+
+    MultiSelect2 = clazz(AbstractSelect2, {
+
+        createContainer: function () {
+            return $("<div></div>", {
+                "class": "select2-container select2-container-multi",
+                "style": "width: " + this.getContainerWidth()
+            }).html([
+                "    <ul class='select2-choices'>",
+                //"<li class='select2-search-choice'><span>California</span><a href="javascript:void(0)" class="select2-search-choice-close"></a></li>" ,
+                "  <li class='select2-search-field'>" ,
+                "    <input type='text' autocomplete='off' style='width: 25px;'>" ,
+                "  </li>" ,
+                "</ul>" ,
+                "<div class='select2-drop' style='display:none;'>" ,
+                "   <ul class='select2-results'>" ,
+                "   </ul>" ,
+                "</div>"].join(""));
+        },
+
+        prepareOpts: function () {
+            var opts = this.parent.prepareOpts.apply(this, arguments);
+
+            opts = $.extend({}, {
+                closeOnSelect: true
+            }, opts);
+
+            // TODO validate placeholder is a string if specified
+
+            if (opts.element.get(0).tagName.toLowerCase() === "select") {
+                // install sthe selection initializer
+                opts.initSelection = function (element) {
+                    var data = [];
+                    element.find(":selected").each(function () {
+                        data.push({id: $(this).attr("value"), text: $(this).text()});
+                    });
+                    return data;
+                };
+            }
+
+            return opts;
+        },
+
+        initContainer: function () {
+
+            var selector = ".select2-choices", selection;
+
+            this.searchContainer = this.container.find(".select2-search-field");
+            this.selection = selection = this.container.find(selector);
+
+            this.search.bind("keydown", this.bind(function (e) {
+                if (e.which === KEY.BACKSPACE && this.search.val() === "") {
+                    this.close();
+
+                    var choices,
+                        selected = selection.find(".select2-search-choice-focus");
+                    if (selected.length > 0) {
+                        this.unselect(selected.first());
+                        this.search.width(10);
+                        killEvent(e);
+                        return;
+                    }
+
+                    choices = selection.find(".select2-search-choice");
+                    if (choices.length > 0) {
+                        choices.last().addClass("select2-search-choice-focus");
+                    }
+                } else {
+                    selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus");
+                }
+
+                if (this.opened()) {
+                    switch (e.which) {
+                    case KEY.UP:
+                    case KEY.DOWN:
+                        this.moveHighlight((e.which === KEY.UP) ? -1 : 1);
+                        killEvent(e);
+                        return;
+                    case KEY.ENTER:
+                        this.selectHighlighted();
+                        killEvent(e);
+                        return;
+                    case KEY.ESC:
+                        this.cancel(e);
+                        e.preventDefault();
+                        return;
+                    }
+                }
+
+                if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.BACKSPACE || e.which === KEY.ESC) {
+                    return;
+                }
+
+                this.open();
+
+                if (e.which === KEY.PAGE_UP || e.which === KEY.PAGE_DOWN) {
+                    // prevent the page from scrolling
+                    killEvent(e);
+                }
+            }));
+
+            this.search.bind("keyup", this.bind(this.resizeSearch));
+
+            this.container.delegate(selector, "click", this.bind(function (e) {
+                this.open();
+                this.focusSearch();
+                e.preventDefault();
+            }));
+
+            this.container.delegate(selector, "focus", this.bind(function () {
+                this.container.addClass("select2-container-active");
+                this.clearPlaceholder();
+            }));
+
+            // set the placeholder if necessary
+            this.clearSearch();
+        },
+
+        initSelection: function () {
+            var data;
+            if (this.opts.element.val() === "") {
+                this.updateSelection([]);
+            }
+            if (this.select || this.opts.element.val() !== "") {
+                data = this.opts.initSelection.call(null, this.opts.element);
+                if (data !== undefined && data !== null) {
+                    this.updateSelection(data);
+                }
+            }
+
+            this.close();
+
+            // set the placeholder if necessary
+            this.clearSearch();
+        },
+
+        clearSearch: function () {
+            var placeholder = this.getPlaceholder();
+
+            if (placeholder !== undefined
+                && this.getVal().length === 0
+                && this.search.hasClass("select2-focused") === false) {
+
+                this.search.val(placeholder).addClass("select2-default");
+                // stretch the search box to full width of the container so as much of the placeholder is visible as possible
+                this.search.width(this.getContainerWidth());
+            } else {
+                this.search.val("").width(10);
+            }
+        },
+
+        clearPlaceholder: function () {
+            if (this.search.hasClass("select2-default")) {
+                this.search.val("").removeClass("select2-default");
+            }
+        },
+
+        open: function () {
+            if (this.opened()) return;
+            this.parent.open.apply(this, arguments);
+            this.resizeSearch();
+            this.focusSearch();
+        },
+
+        close: function () {
+            if (!this.opened()) return;
+            this.parent.close.apply(this, arguments);
+        },
+
+        focus: function () {
+            this.close();
+            this.search.focus();
+        },
+
+        isFocused: function () {
+            return this.search.hasClass("select2-focused");
+        },
+
+        updateSelection: function (data) {
+            var ids = [], filtered = [], self = this;
+
+            // filter out duplicates
+            $(data).each(function () {
+                if (indexOf(self.id(this), ids) < 0) {
+                    ids.push(self.id(this));
+                    filtered.push(this);
+                }
+            });
+            data = filtered;
+
+            this.selection.find(".select2-search-choice").remove();
+            $(data).each(function () {
+                self.addSelectedChoice(this);
+            });
+            self.postprocessResults();
+        },
+
+        onSelect: function (data) {
+            this.addSelectedChoice(data);
+            if (this.select) { this.postprocessResults(); }
+
+            if (this.opts.closeOnSelect) {
+                this.close();
+                this.search.width(10);
+            } else {
+                this.search.width(10);
+                this.resizeSearch();
+            }
+
+            // since its not possible to select an element that has already been
+            // added we do not need to check if this is a new element before firing change
+            this.triggerChange();
+
+            this.focusSearch();
+        },
+
+        cancel: function () {
+            this.close();
+            this.focusSearch();
+        },
+
+        addSelectedChoice: function (data) {
+            var choice,
+                id = this.id(data),
+                parts,
+                val = this.getVal();
+
+            parts = ["<li class='select2-search-choice'>",
+                this.opts.formatSelection(data),
+                "<a href='javascript:void(0)' class='select2-search-choice-close' tabindex='-1'></a>",
+                "</li>"
+            ];
+
+            choice = $(parts.join(""));
+            choice.find("a")
+                .bind("click dblclick", this.bind(function (e) {
+                this.unselect($(e.target));
+                this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus");
+                killEvent(e);
+                this.close();
+                this.focusSearch();
+            })).bind("focus", this.bind(function () {
+                this.container.addClass("select2-container-active");
+            }));
+
+            choice.data("select2-data", data);
+            choice.insertBefore(this.searchContainer);
+
+            val.push(id);
+            this.setVal(val);
+        },
+
+        unselect: function (selected) {
+            var val = this.getVal(),
+                index;
+
+            selected = selected.closest(".select2-search-choice");
+
+            if (selected.length === 0) {
+                throw "Invalid argument: " + selected + ". Must be .select2-search-choice";
+            }
+
+            index = indexOf(this.id(selected.data("select2-data")), val);
+
+            if (index >= 0) {
+                val.splice(index, 1);
+                this.setVal(val);
+                if (this.select) this.postprocessResults();
+            }
+            selected.remove();
+            this.triggerChange();
+        },
+
+        postprocessResults: function () {
+            var val = this.getVal(),
+                choices = this.results.find(".select2-result"),
+                self = this;
+
+            choices.each(function () {
+                var choice = $(this), id = self.id(choice.data("select2-data"));
+                if (indexOf(id, val) >= 0) {
+                    choice.addClass("select2-disabled");
+                } else {
+                    choice.removeClass("select2-disabled");
+                }
+            });
+
+            choices.each(function (i) {
+                if (!$(this).hasClass("select2-disabled")) {
+                    self.highlight(i);
+                    return false;
+                }
+            });
+
+        }