From f0b3688fefdd63dcbec818a310acdb8222294b99 Mon Sep 17 00:00:00 2001 From: jpic Date: Mon, 22 May 2023 12:20:33 +0200 Subject: [PATCH] Apply automatic changes --- .github/workflows/release.yml | 42 + .gitignore | 20 + .openshift/README.md | 5 + .openshift/action_hooks/README.md | 3 + .openshift/action_hooks/deploy | 27 + .openshift/cron/README.cron | 27 + .openshift/cron/hourly/reset_database | 9 + .readthedocs.yaml | 23 + .travis.yml | 65 + AUTHORS | 90 + CHANGELOG | 1300 ++++ CHANGES.txt | 1 + LICENSE | 22 + MANIFEST.in | 4 + README | 50 + README.rst | 1 + changelog.py | 91 + conftest.py | 8 + docs/Makefile | 216 + docs/_ext/djangodocs.py | 21 + docs/api.rst | 154 + docs/conf.py | 379 + docs/genericm2m.rst | 67 + docs/gfk.rst | 112 + docs/gm2m.rst | 66 + docs/img/all.png | Bin 0 -> 3884 bytes docs/img/autocomplete.png | Bin 0 -> 2613 bytes docs/img/create_option.png | Bin 0 -> 3762 bytes docs/img/created_option.png | Bin 0 -> 1625 bytes docs/img/mine.png | Bin 0 -> 2996 bytes docs/img/view.png | Bin 0 -> 9178 bytes docs/index.rst | 46 + docs/install.rst | 67 + docs/requirements.txt | 3 + docs/tagging.rst | 74 + docs/taggit.rst | 81 + docs/tutorial.rst | 864 +++ package-lock.json | 2400 ++++++ package.json | 36 + pytest.ini | 3 + release.sh | 45 + select2.build.js | 46 + setup.py | 56 + src/__init__.py | 1 + src/dal/__init__.py | 5 + src/dal/autocomplete.py | 92 + src/dal/forms.py | 178 + src/dal/forward.py | 169 + .../autocomplete_light/autocomplete_light.js | 485 ++ .../autocomplete_light.min.js | 1 + .../autocomplete_light.min.js.map | 1 + src/dal/static/autocomplete_light/i18n/af.js | 2 + src/dal/static/autocomplete_light/i18n/ar.js | 2 + src/dal/static/autocomplete_light/i18n/az.js | 2 + src/dal/static/autocomplete_light/i18n/bg.js | 2 + src/dal/static/autocomplete_light/i18n/bn.js | 2 + src/dal/static/autocomplete_light/i18n/bs.js | 2 + src/dal/static/autocomplete_light/i18n/ca.js | 2 + src/dal/static/autocomplete_light/i18n/cs.js | 2 + src/dal/static/autocomplete_light/i18n/da.js | 2 + src/dal/static/autocomplete_light/i18n/de.js | 2 + src/dal/static/autocomplete_light/i18n/dsb.js | 2 + src/dal/static/autocomplete_light/i18n/el.js | 2 + src/dal/static/autocomplete_light/i18n/en.js | 2 + src/dal/static/autocomplete_light/i18n/eo.js | 2 + src/dal/static/autocomplete_light/i18n/es.js | 2 + src/dal/static/autocomplete_light/i18n/et.js | 2 + src/dal/static/autocomplete_light/i18n/eu.js | 2 + src/dal/static/autocomplete_light/i18n/fa.js | 2 + src/dal/static/autocomplete_light/i18n/fi.js | 2 + src/dal/static/autocomplete_light/i18n/fr.js | 2 + src/dal/static/autocomplete_light/i18n/gl.js | 2 + src/dal/static/autocomplete_light/i18n/he.js | 2 + src/dal/static/autocomplete_light/i18n/hi.js | 2 + src/dal/static/autocomplete_light/i18n/hr.js | 2 + src/dal/static/autocomplete_light/i18n/hsb.js | 2 + src/dal/static/autocomplete_light/i18n/hu.js | 2 + src/dal/static/autocomplete_light/i18n/hy.js | 2 + src/dal/static/autocomplete_light/i18n/id.js | 2 + src/dal/static/autocomplete_light/i18n/is.js | 2 + src/dal/static/autocomplete_light/i18n/it.js | 2 + src/dal/static/autocomplete_light/i18n/ja.js | 2 + src/dal/static/autocomplete_light/i18n/ka.js | 2 + src/dal/static/autocomplete_light/i18n/km.js | 2 + src/dal/static/autocomplete_light/i18n/ko.js | 2 + src/dal/static/autocomplete_light/i18n/lt.js | 2 + src/dal/static/autocomplete_light/i18n/lv.js | 2 + src/dal/static/autocomplete_light/i18n/mk.js | 2 + src/dal/static/autocomplete_light/i18n/ms.js | 2 + src/dal/static/autocomplete_light/i18n/nb.js | 2 + src/dal/static/autocomplete_light/i18n/ne.js | 2 + src/dal/static/autocomplete_light/i18n/nl.js | 2 + src/dal/static/autocomplete_light/i18n/pa.js | 2 + src/dal/static/autocomplete_light/i18n/pl.js | 2 + src/dal/static/autocomplete_light/i18n/ps.js | 2 + .../static/autocomplete_light/i18n/pt-BR.js | 2 + src/dal/static/autocomplete_light/i18n/pt.js | 2 + src/dal/static/autocomplete_light/i18n/ro.js | 2 + src/dal/static/autocomplete_light/i18n/ru.js | 2 + src/dal/static/autocomplete_light/i18n/sk.js | 2 + src/dal/static/autocomplete_light/i18n/sl.js | 2 + src/dal/static/autocomplete_light/i18n/sq.js | 2 + .../static/autocomplete_light/i18n/sr-Cyrl.js | 2 + src/dal/static/autocomplete_light/i18n/sr.js | 2 + src/dal/static/autocomplete_light/i18n/sv.js | 2 + src/dal/static/autocomplete_light/i18n/te.js | 2 + src/dal/static/autocomplete_light/i18n/th.js | 2 + src/dal/static/autocomplete_light/i18n/tk.js | 2 + src/dal/static/autocomplete_light/i18n/tr.js | 2 + src/dal/static/autocomplete_light/i18n/uk.js | 2 + src/dal/static/autocomplete_light/i18n/vi.js | 2 + .../static/autocomplete_light/i18n/zh-CN.js | 2 + .../static/autocomplete_light/i18n/zh-TW.js | 2 + src/dal/test/__init__.py | 1 + src/dal/test/case.py | 106 + src/dal/test/stories.py | 426 ++ src/dal/test/utils.py | 64 + src/dal/views.py | 223 + src/dal/widgets.py | 193 + src/dal_contenttypes/__init__.py | 1 + src/dal_contenttypes/fields.py | 63 + src/dal_genericm2m/__init__.py | 1 + src/dal_genericm2m/fields.py | 20 + .../__init__.py | 1 + .../fields.py | 10 + src/dal_gm2m/__init__.py | 1 + src/dal_gm2m/fields.py | 16 + src/dal_gm2m_queryset_sequence/__init__.py | 1 + src/dal_gm2m_queryset_sequence/fields.py | 10 + src/dal_legacy_static/__init__.py | 1 + .../static/admin/css/autocomplete.css | 260 + .../css/vendor/select2/LICENSE-SELECT2.md | 21 + .../admin/css/vendor/select2/select2.css | 486 ++ .../admin/css/vendor/select2/select2.min.css | 1 + .../static/admin/js/autocomplete.js | 37 + .../static/admin/js/vendor/select2/LICENSE.md | 21 + .../static/admin/js/vendor/select2/i18n/ar.js | 3 + .../static/admin/js/vendor/select2/i18n/az.js | 3 + .../static/admin/js/vendor/select2/i18n/bg.js | 3 + .../static/admin/js/vendor/select2/i18n/ca.js | 3 + .../static/admin/js/vendor/select2/i18n/cs.js | 3 + .../static/admin/js/vendor/select2/i18n/da.js | 3 + .../static/admin/js/vendor/select2/i18n/de.js | 3 + .../static/admin/js/vendor/select2/i18n/el.js | 3 + .../static/admin/js/vendor/select2/i18n/en.js | 3 + .../static/admin/js/vendor/select2/i18n/es.js | 3 + .../static/admin/js/vendor/select2/i18n/et.js | 3 + .../static/admin/js/vendor/select2/i18n/eu.js | 3 + .../static/admin/js/vendor/select2/i18n/fa.js | 3 + .../static/admin/js/vendor/select2/i18n/fi.js | 3 + .../static/admin/js/vendor/select2/i18n/fr.js | 3 + .../static/admin/js/vendor/select2/i18n/gl.js | 3 + .../static/admin/js/vendor/select2/i18n/he.js | 3 + .../static/admin/js/vendor/select2/i18n/hi.js | 3 + .../static/admin/js/vendor/select2/i18n/hr.js | 3 + .../static/admin/js/vendor/select2/i18n/hu.js | 3 + .../static/admin/js/vendor/select2/i18n/id.js | 3 + .../static/admin/js/vendor/select2/i18n/is.js | 3 + .../static/admin/js/vendor/select2/i18n/it.js | 3 + .../static/admin/js/vendor/select2/i18n/ja.js | 3 + .../static/admin/js/vendor/select2/i18n/km.js | 3 + .../static/admin/js/vendor/select2/i18n/ko.js | 3 + .../static/admin/js/vendor/select2/i18n/lt.js | 3 + .../static/admin/js/vendor/select2/i18n/lv.js | 3 + .../static/admin/js/vendor/select2/i18n/mk.js | 3 + .../static/admin/js/vendor/select2/i18n/ms.js | 3 + .../static/admin/js/vendor/select2/i18n/nb.js | 3 + .../static/admin/js/vendor/select2/i18n/nl.js | 3 + .../static/admin/js/vendor/select2/i18n/pl.js | 3 + .../admin/js/vendor/select2/i18n/pt-BR.js | 3 + .../static/admin/js/vendor/select2/i18n/pt.js | 3 + .../static/admin/js/vendor/select2/i18n/ro.js | 3 + .../static/admin/js/vendor/select2/i18n/ru.js | 3 + .../static/admin/js/vendor/select2/i18n/sk.js | 3 + .../admin/js/vendor/select2/i18n/sr-Cyrl.js | 3 + .../static/admin/js/vendor/select2/i18n/sr.js | 3 + .../static/admin/js/vendor/select2/i18n/sv.js | 3 + .../static/admin/js/vendor/select2/i18n/th.js | 3 + .../static/admin/js/vendor/select2/i18n/tr.js | 3 + .../static/admin/js/vendor/select2/i18n/uk.js | 3 + .../static/admin/js/vendor/select2/i18n/vi.js | 3 + .../admin/js/vendor/select2/i18n/zh-CN.js | 3 + .../admin/js/vendor/select2/i18n/zh-TW.js | 3 + .../admin/js/vendor/select2/select2.full.js | 6436 +++++++++++++++++ .../js/vendor/select2/select2.full.min.js | 3 + src/dal_queryset_sequence/__init__.py | 1 + src/dal_queryset_sequence/fields.py | 174 + src/dal_queryset_sequence/tests/test_views.py | 54 + src/dal_queryset_sequence/views.py | 72 + src/dal_queryset_sequence/widgets.py | 51 + src/dal_select2/__init__.py | 5 + src/dal_select2/apps.py | 12 + src/dal_select2/fields.py | 52 + .../locale/bg/LC_MESSAGES/django.mo | Bin 0 -> 508 bytes .../locale/bg/LC_MESSAGES/django.po | 22 + .../locale/cs/LC_MESSAGES/django.mo | Bin 0 -> 528 bytes .../locale/cs/LC_MESSAGES/django.po | 22 + .../locale/de/LC_MESSAGES/django.mo | Bin 0 -> 501 bytes .../locale/de/LC_MESSAGES/django.po | 22 + .../locale/es/LC_MESSAGES/django.mo | Bin 0 -> 497 bytes .../locale/es/LC_MESSAGES/django.po | 22 + .../locale/fi/LC_MESSAGES/django.mo | Bin 0 -> 488 bytes .../locale/fi/LC_MESSAGES/django.po | 22 + .../locale/fr/LC_MESSAGES/django.mo | Bin 0 -> 497 bytes .../locale/fr/LC_MESSAGES/django.po | 22 + .../locale/it/LC_MESSAGES/django.mo | Bin 0 -> 498 bytes .../locale/it/LC_MESSAGES/django.po | 22 + .../locale/ja/LC_MESSAGES/django.mo | Bin 0 -> 497 bytes .../locale/ja/LC_MESSAGES/django.po | 22 + .../locale/nl/LC_MESSAGES/django.mo | Bin 0 -> 500 bytes .../locale/nl/LC_MESSAGES/django.po | 22 + .../locale/pl/LC_MESSAGES/django.mo | Bin 0 -> 557 bytes .../locale/pl/LC_MESSAGES/django.po | 23 + .../locale/pt-br/LC_MESSAGES/django.mo | Bin 0 -> 503 bytes .../locale/pt-br/LC_MESSAGES/django.po | 23 + .../locale/ru/LC_MESSAGES/django.mo | Bin 0 -> 644 bytes .../locale/ru/LC_MESSAGES/django.po | 24 + .../locale/tr/LC_MESSAGES/django.mo | Bin 0 -> 492 bytes .../locale/tr/LC_MESSAGES/django.po | 23 + .../locale/uk/LC_MESSAGES/django.mo | Bin 0 -> 582 bytes .../locale/uk/LC_MESSAGES/django.po | 23 + .../locale/zh-cn/LC_MESSAGES/django.mo | Bin 0 -> 458 bytes .../locale/zh-cn/LC_MESSAGES/django.po | 21 + src/dal_select2/models.py | 1 + .../static/autocomplete_light/select2.css | 87 + .../static/autocomplete_light/select2.js | 146 + .../static/autocomplete_light/select2.min.js | 1 + .../autocomplete_light/select2.min.js.map | 1 + src/dal_select2/static/vendor/select2 | 1 + src/dal_select2/test.py | 32 + src/dal_select2/views.py | 298 + src/dal_select2/widgets.py | 197 + src/dal_select2_queryset_sequence/__init__.py | 14 + src/dal_select2_queryset_sequence/fields.py | 74 + src/dal_select2_queryset_sequence/views.py | 108 + src/dal_select2_queryset_sequence/widgets.py | 26 + src/dal_select2_tagging/__init__.py | 1 + src/dal_select2_tagging/widgets.py | 28 + src/dal_select2_taggit/__init__.py | 1 + src/dal_select2_taggit/widgets.py | 56 + test_project/.coveragerc | 3 + test_project/custom_select2/__init__.py | 1 + test_project/custom_select2/admin.py | 16 + test_project/custom_select2/apps.py | 11 + test_project/custom_select2/forms.py | 13 + .../custom_select2/migrations/0001_initial.py | 24 + .../custom_select2/migrations/__init__.py | 0 test_project/custom_select2/models.py | 27 + .../custom_select2/static/t_select2.js | 22 + .../custom_select2/test_functional.py | 18 + test_project/custom_select2/urls.py | 14 + test_project/custom_select2/widgets.py | 13 + .../forward_different_fields/__init__.py | 0 .../forward_different_fields/admin.py | 16 + test_project/forward_different_fields/apps.py | 5 + .../forward_different_fields/forms.py | 67 + .../migrations/0001_initial.py | 21 + .../migrations/__init__.py | 0 .../forward_different_fields/models.py | 11 + .../static/js_handlers.js | 12 + .../forward_different_fields/tests.py | 1 + test_project/forward_different_fields/urls.py | 27 + test_project/linked_data/__init__.py | 1 + test_project/linked_data/admin.py | 16 + test_project/linked_data/apps.py | 11 + test_project/linked_data/forms.py | 29 + .../linked_data/migrations/0001_initial.py | 27 + .../linked_data/migrations/__init__.py | 0 test_project/linked_data/models.py | 35 + .../linked_data/static/linked_data.js | 7 + test_project/linked_data/test_forms.py | 53 + test_project/linked_data/test_functional.py | 69 + test_project/linked_data/test_views.py | 52 + test_project/linked_data/urls.py | 25 + test_project/manage.py | 10 + test_project/rename_forward/__init__.py | 1 + test_project/rename_forward/admin.py | 16 + test_project/rename_forward/apps.py | 5 + test_project/rename_forward/forms.py | 24 + .../rename_forward/migrations/0001_initial.py | 27 + .../rename_forward/migrations/__init__.py | 0 test_project/rename_forward/models.py | 35 + .../rename_forward/static/linked_data.js | 6 + .../rename_forward/test_functional.py | 11 + test_project/rename_forward/urls.py | 30 + test_project/requirements.txt | 21 + test_project/secure_data/__init__.py | 1 + test_project/secure_data/admin.py | 32 + test_project/secure_data/apps.py | 11 + test_project/secure_data/forms.py | 14 + .../secure_data/migrations/0001_initial.py | 27 + .../secure_data/migrations/__init__.py | 0 test_project/secure_data/models.py | 35 + test_project/secure_data/test_functional.py | 32 + test_project/secure_data/urls.py | 19 + test_project/secure_data/views.py | 0 .../select2_djhacker_formfield/__init__.py | 1 + .../select2_djhacker_formfield/admin.py | 13 + .../select2_djhacker_formfield/apps.py | 11 + .../migrations/0001_initial.py | 24 + .../migrations/__init__.py | 0 .../select2_djhacker_formfield/models.py | 24 + .../test_functional.py | 7 + .../select2_djhacker_formfield/urls.py | 23 + test_project/select2_foreign_key/__init__.py | 1 + test_project/select2_foreign_key/admin.py | 16 + test_project/select2_foreign_key/apps.py | 11 + test_project/select2_foreign_key/forms.py | 14 + .../migrations/0001_initial.py | 24 + .../migrations/__init__.py | 0 test_project/select2_foreign_key/models.py | 24 + .../select2_foreign_key/test_functional.py | 67 + test_project/select2_foreign_key/urls.py | 14 + .../select2_generic_foreign_key/__init__.py | 1 + .../select2_generic_foreign_key/admin.py | 16 + .../select2_generic_foreign_key/apps.py | 12 + .../select2_generic_foreign_key/forms.py | 31 + .../migrations/0001_initial.py | 38 + .../migrations/__init__.py | 0 .../select2_generic_foreign_key/models.py | 63 + .../select2_generic_foreign_key/test_forms.py | 107 + .../test_functional.py | 42 + .../select2_generic_foreign_key/test_views.py | 10 + .../select2_generic_foreign_key/urls.py | 20 + .../select2_generic_foreign_key/views.py | 13 + test_project/select2_generic_m2m/__init__.py | 1 + test_project/select2_generic_m2m/admin.py | 16 + test_project/select2_generic_m2m/apps.py | 12 + test_project/select2_generic_m2m/forms.py | 21 + test_project/select2_generic_m2m/models.py | 23 + .../select2_generic_m2m/test_forms.py | 106 + .../select2_generic_m2m/test_functional.py | 40 + test_project/select2_generic_m2m/urls.py | 29 + test_project/select2_generic_m2m/views.py | 0 test_project/select2_gm2m/__init__.py | 1 + test_project/select2_gm2m/admin.py | 16 + test_project/select2_gm2m/apps.py | 12 + test_project/select2_gm2m/forms.py | 21 + test_project/select2_gm2m/models.py | 23 + test_project/select2_gm2m/test_forms.py | 18 + test_project/select2_gm2m/test_functional.py | 15 + test_project/select2_gm2m/urls.py | 29 + test_project/select2_gm2m/views.py | 0 test_project/select2_list/__init__.py | 0 test_project/select2_list/admin.py | 16 + test_project/select2_list/forms.py | 21 + .../select2_list/migrations/0001_initial.py | 24 + .../select2_list/migrations/__init__.py | 0 test_project/select2_list/models.py | 25 + test_project/select2_list/test_fields.py | 79 + test_project/select2_list/test_functional.py | 66 + test_project/select2_list/test_views.py | 274 + test_project/select2_list/urls.py | 17 + test_project/select2_list/views.py | 19 + test_project/select2_many_to_many/__init__.py | 1 + test_project/select2_many_to_many/admin.py | 16 + test_project/select2_many_to_many/apps.py | 11 + test_project/select2_many_to_many/forms.py | 16 + .../migrations/0001_initial.py | 24 + .../migrations/__init__.py | 0 test_project/select2_many_to_many/models.py | 25 + .../select2_many_to_many/test_functional.py | 40 + test_project/select2_many_to_many/urls.py | 26 + test_project/select2_nestedadmin/__init__.py | 1 + test_project/select2_nestedadmin/admin.py | 26 + test_project/select2_nestedadmin/apps.py | 5 + test_project/select2_nestedadmin/forms.py | 17 + .../migrations/0001_initial.py | 49 + .../migrations/__init__.py | 0 test_project/select2_nestedadmin/models.py | 53 + .../select2_nestedadmin/test_functional.py | 43 + test_project/select2_nestedadmin/urls.py | 32 + test_project/select2_one_to_one/__init__.py | 1 + test_project/select2_one_to_one/admin.py | 16 + test_project/select2_one_to_one/apps.py | 11 + test_project/select2_one_to_one/forms.py | 14 + .../migrations/0001_initial.py | 24 + .../select2_one_to_one/migrations/__init__.py | 0 test_project/select2_one_to_one/models.py | 31 + .../select2_one_to_one/test_functional.py | 45 + test_project/select2_one_to_one/urls.py | 27 + .../select2_outside_admin/__init__.py | 0 .../templates/select2_outside_admin.html | 51 + test_project/select2_outside_admin/urls.py | 12 + test_project/select2_outside_admin/views.py | 50 + test_project/select2_tagging/__init__.py | 0 test_project/select2_tagging/admin.py | 16 + test_project/select2_tagging/forms.py | 18 + test_project/select2_tagging/models.py | 23 + test_project/select2_tagging/test_forms.py | 65 + .../select2_tagging/test_functional.py | 40 + test_project/select2_tagging/urls.py | 16 + test_project/select2_taggit/__init__.py | 0 test_project/select2_taggit/admin.py | 16 + test_project/select2_taggit/forms.py | 14 + .../select2_taggit/migrations/0001_initial.py | 26 + .../select2_taggit/migrations/__init__.py | 0 test_project/select2_taggit/models.py | 23 + test_project/select2_taggit/test_forms.py | 85 + .../select2_taggit/test_functional.py | 51 + test_project/select2_taggit/urls.py | 16 + test_project/settings/__init__.py | 1 + test_project/settings/base.py | 201 + test_project/templates/admin/login.html | 71 + test_project/templates/base.html | 69 + test_project/tests/__init__.py | 0 test_project/tests/admin.py | 4 + test_project/tests/models.py | 15 + test_project/tests/test_widgets.py | 162 + test_project/urls.py | 48 + test_project/views.py | 85 + test_project/wsgi.py | 22 + tox.ini | 63 + 413 files changed, 22324 insertions(+) create mode 100644 .github/workflows/release.yml create mode 100644 .gitignore create mode 100644 .openshift/README.md create mode 100644 .openshift/action_hooks/README.md create mode 100755 .openshift/action_hooks/deploy create mode 100644 .openshift/cron/README.cron create mode 100755 .openshift/cron/hourly/reset_database create mode 100644 .readthedocs.yaml create mode 100644 .travis.yml create mode 100644 AUTHORS create mode 100644 CHANGELOG create mode 120000 CHANGES.txt create mode 100644 LICENSE create mode 100644 MANIFEST.in create mode 100644 README create mode 120000 README.rst create mode 100644 changelog.py create mode 100644 conftest.py create mode 100644 docs/Makefile create mode 100644 docs/_ext/djangodocs.py create mode 100644 docs/api.rst create mode 100644 docs/conf.py create mode 100644 docs/genericm2m.rst create mode 100644 docs/gfk.rst create mode 100644 docs/gm2m.rst create mode 100644 docs/img/all.png create mode 100644 docs/img/autocomplete.png create mode 100644 docs/img/create_option.png create mode 100644 docs/img/created_option.png create mode 100644 docs/img/mine.png create mode 100644 docs/img/view.png create mode 100644 docs/index.rst create mode 100644 docs/install.rst create mode 100644 docs/requirements.txt create mode 100644 docs/tagging.rst create mode 100644 docs/taggit.rst create mode 100644 docs/tutorial.rst create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 pytest.ini create mode 100755 release.sh create mode 100644 select2.build.js create mode 100644 setup.py create mode 100644 src/__init__.py create mode 100644 src/dal/__init__.py create mode 100644 src/dal/autocomplete.py create mode 100644 src/dal/forms.py create mode 100644 src/dal/forward.py create mode 100644 src/dal/static/autocomplete_light/autocomplete_light.js create mode 100644 src/dal/static/autocomplete_light/autocomplete_light.min.js create mode 100644 src/dal/static/autocomplete_light/autocomplete_light.min.js.map create mode 100644 src/dal/static/autocomplete_light/i18n/af.js create mode 100644 src/dal/static/autocomplete_light/i18n/ar.js create mode 100644 src/dal/static/autocomplete_light/i18n/az.js create mode 100644 src/dal/static/autocomplete_light/i18n/bg.js create mode 100644 src/dal/static/autocomplete_light/i18n/bn.js create mode 100644 src/dal/static/autocomplete_light/i18n/bs.js create mode 100644 src/dal/static/autocomplete_light/i18n/ca.js create mode 100644 src/dal/static/autocomplete_light/i18n/cs.js create mode 100644 src/dal/static/autocomplete_light/i18n/da.js create mode 100644 src/dal/static/autocomplete_light/i18n/de.js create mode 100644 src/dal/static/autocomplete_light/i18n/dsb.js create mode 100644 src/dal/static/autocomplete_light/i18n/el.js create mode 100644 src/dal/static/autocomplete_light/i18n/en.js create mode 100644 src/dal/static/autocomplete_light/i18n/eo.js create mode 100644 src/dal/static/autocomplete_light/i18n/es.js create mode 100644 src/dal/static/autocomplete_light/i18n/et.js create mode 100644 src/dal/static/autocomplete_light/i18n/eu.js create mode 100644 src/dal/static/autocomplete_light/i18n/fa.js create mode 100644 src/dal/static/autocomplete_light/i18n/fi.js create mode 100644 src/dal/static/autocomplete_light/i18n/fr.js create mode 100644 src/dal/static/autocomplete_light/i18n/gl.js create mode 100644 src/dal/static/autocomplete_light/i18n/he.js create mode 100644 src/dal/static/autocomplete_light/i18n/hi.js create mode 100644 src/dal/static/autocomplete_light/i18n/hr.js create mode 100644 src/dal/static/autocomplete_light/i18n/hsb.js create mode 100644 src/dal/static/autocomplete_light/i18n/hu.js create mode 100644 src/dal/static/autocomplete_light/i18n/hy.js create mode 100644 src/dal/static/autocomplete_light/i18n/id.js create mode 100644 src/dal/static/autocomplete_light/i18n/is.js create mode 100644 src/dal/static/autocomplete_light/i18n/it.js create mode 100644 src/dal/static/autocomplete_light/i18n/ja.js create mode 100644 src/dal/static/autocomplete_light/i18n/ka.js create mode 100644 src/dal/static/autocomplete_light/i18n/km.js create mode 100644 src/dal/static/autocomplete_light/i18n/ko.js create mode 100644 src/dal/static/autocomplete_light/i18n/lt.js create mode 100644 src/dal/static/autocomplete_light/i18n/lv.js create mode 100644 src/dal/static/autocomplete_light/i18n/mk.js create mode 100644 src/dal/static/autocomplete_light/i18n/ms.js create mode 100644 src/dal/static/autocomplete_light/i18n/nb.js create mode 100644 src/dal/static/autocomplete_light/i18n/ne.js create mode 100644 src/dal/static/autocomplete_light/i18n/nl.js create mode 100644 src/dal/static/autocomplete_light/i18n/pa.js create mode 100644 src/dal/static/autocomplete_light/i18n/pl.js create mode 100644 src/dal/static/autocomplete_light/i18n/ps.js create mode 100644 src/dal/static/autocomplete_light/i18n/pt-BR.js create mode 100644 src/dal/static/autocomplete_light/i18n/pt.js create mode 100644 src/dal/static/autocomplete_light/i18n/ro.js create mode 100644 src/dal/static/autocomplete_light/i18n/ru.js create mode 100644 src/dal/static/autocomplete_light/i18n/sk.js create mode 100644 src/dal/static/autocomplete_light/i18n/sl.js create mode 100644 src/dal/static/autocomplete_light/i18n/sq.js create mode 100644 src/dal/static/autocomplete_light/i18n/sr-Cyrl.js create mode 100644 src/dal/static/autocomplete_light/i18n/sr.js create mode 100644 src/dal/static/autocomplete_light/i18n/sv.js create mode 100644 src/dal/static/autocomplete_light/i18n/te.js create mode 100644 src/dal/static/autocomplete_light/i18n/th.js create mode 100644 src/dal/static/autocomplete_light/i18n/tk.js create mode 100644 src/dal/static/autocomplete_light/i18n/tr.js create mode 100644 src/dal/static/autocomplete_light/i18n/uk.js create mode 100644 src/dal/static/autocomplete_light/i18n/vi.js create mode 100644 src/dal/static/autocomplete_light/i18n/zh-CN.js create mode 100644 src/dal/static/autocomplete_light/i18n/zh-TW.js create mode 100644 src/dal/test/__init__.py create mode 100644 src/dal/test/case.py create mode 100644 src/dal/test/stories.py create mode 100644 src/dal/test/utils.py create mode 100644 src/dal/views.py create mode 100644 src/dal/widgets.py create mode 100644 src/dal_contenttypes/__init__.py create mode 100644 src/dal_contenttypes/fields.py create mode 100644 src/dal_genericm2m/__init__.py create mode 100644 src/dal_genericm2m/fields.py create mode 100644 src/dal_genericm2m_queryset_sequence/__init__.py create mode 100644 src/dal_genericm2m_queryset_sequence/fields.py create mode 100644 src/dal_gm2m/__init__.py create mode 100644 src/dal_gm2m/fields.py create mode 100644 src/dal_gm2m_queryset_sequence/__init__.py create mode 100644 src/dal_gm2m_queryset_sequence/fields.py create mode 100644 src/dal_legacy_static/__init__.py create mode 100644 src/dal_legacy_static/static/admin/css/autocomplete.css create mode 100755 src/dal_legacy_static/static/admin/css/vendor/select2/LICENSE-SELECT2.md create mode 100755 src/dal_legacy_static/static/admin/css/vendor/select2/select2.css create mode 100755 src/dal_legacy_static/static/admin/css/vendor/select2/select2.min.css create mode 100644 src/dal_legacy_static/static/admin/js/autocomplete.js create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/LICENSE.md create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/i18n/ar.js create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/i18n/az.js create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/i18n/bg.js create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/i18n/ca.js create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/i18n/cs.js create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/i18n/da.js create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/i18n/de.js create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/i18n/el.js create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/i18n/en.js create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/i18n/es.js create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/i18n/et.js create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/i18n/eu.js create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/i18n/fa.js create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/i18n/fi.js create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/i18n/fr.js create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/i18n/gl.js create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/i18n/he.js create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/i18n/hi.js create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/i18n/hr.js create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/i18n/hu.js create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/i18n/id.js create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/i18n/is.js create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/i18n/it.js create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/i18n/ja.js create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/i18n/km.js create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/i18n/ko.js create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/i18n/lt.js create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/i18n/lv.js create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/i18n/mk.js create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/i18n/ms.js create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/i18n/nb.js create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/i18n/nl.js create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/i18n/pl.js create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/i18n/pt-BR.js create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/i18n/pt.js create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/i18n/ro.js create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/i18n/ru.js create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/i18n/sk.js create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/i18n/sr-Cyrl.js create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/i18n/sr.js create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/i18n/sv.js create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/i18n/th.js create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/i18n/tr.js create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/i18n/uk.js create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/i18n/vi.js create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/i18n/zh-CN.js create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/i18n/zh-TW.js create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/select2.full.js create mode 100755 src/dal_legacy_static/static/admin/js/vendor/select2/select2.full.min.js create mode 100644 src/dal_queryset_sequence/__init__.py create mode 100644 src/dal_queryset_sequence/fields.py create mode 100644 src/dal_queryset_sequence/tests/test_views.py create mode 100644 src/dal_queryset_sequence/views.py create mode 100644 src/dal_queryset_sequence/widgets.py create mode 100644 src/dal_select2/__init__.py create mode 100644 src/dal_select2/apps.py create mode 100644 src/dal_select2/fields.py create mode 100644 src/dal_select2/locale/bg/LC_MESSAGES/django.mo create mode 100644 src/dal_select2/locale/bg/LC_MESSAGES/django.po create mode 100644 src/dal_select2/locale/cs/LC_MESSAGES/django.mo create mode 100644 src/dal_select2/locale/cs/LC_MESSAGES/django.po create mode 100644 src/dal_select2/locale/de/LC_MESSAGES/django.mo create mode 100644 src/dal_select2/locale/de/LC_MESSAGES/django.po create mode 100644 src/dal_select2/locale/es/LC_MESSAGES/django.mo create mode 100644 src/dal_select2/locale/es/LC_MESSAGES/django.po create mode 100644 src/dal_select2/locale/fi/LC_MESSAGES/django.mo create mode 100644 src/dal_select2/locale/fi/LC_MESSAGES/django.po create mode 100644 src/dal_select2/locale/fr/LC_MESSAGES/django.mo create mode 100644 src/dal_select2/locale/fr/LC_MESSAGES/django.po create mode 100644 src/dal_select2/locale/it/LC_MESSAGES/django.mo create mode 100644 src/dal_select2/locale/it/LC_MESSAGES/django.po create mode 100644 src/dal_select2/locale/ja/LC_MESSAGES/django.mo create mode 100644 src/dal_select2/locale/ja/LC_MESSAGES/django.po create mode 100644 src/dal_select2/locale/nl/LC_MESSAGES/django.mo create mode 100644 src/dal_select2/locale/nl/LC_MESSAGES/django.po create mode 100644 src/dal_select2/locale/pl/LC_MESSAGES/django.mo create mode 100644 src/dal_select2/locale/pl/LC_MESSAGES/django.po create mode 100644 src/dal_select2/locale/pt-br/LC_MESSAGES/django.mo create mode 100644 src/dal_select2/locale/pt-br/LC_MESSAGES/django.po create mode 100644 src/dal_select2/locale/ru/LC_MESSAGES/django.mo create mode 100644 src/dal_select2/locale/ru/LC_MESSAGES/django.po create mode 100644 src/dal_select2/locale/tr/LC_MESSAGES/django.mo create mode 100644 src/dal_select2/locale/tr/LC_MESSAGES/django.po create mode 100644 src/dal_select2/locale/uk/LC_MESSAGES/django.mo create mode 100644 src/dal_select2/locale/uk/LC_MESSAGES/django.po create mode 100644 src/dal_select2/locale/zh-cn/LC_MESSAGES/django.mo create mode 100644 src/dal_select2/locale/zh-cn/LC_MESSAGES/django.po create mode 100644 src/dal_select2/models.py create mode 100644 src/dal_select2/static/autocomplete_light/select2.css create mode 100644 src/dal_select2/static/autocomplete_light/select2.js create mode 100644 src/dal_select2/static/autocomplete_light/select2.min.js create mode 100644 src/dal_select2/static/autocomplete_light/select2.min.js.map create mode 160000 src/dal_select2/static/vendor/select2 create mode 100644 src/dal_select2/test.py create mode 100644 src/dal_select2/views.py create mode 100644 src/dal_select2/widgets.py create mode 100644 src/dal_select2_queryset_sequence/__init__.py create mode 100644 src/dal_select2_queryset_sequence/fields.py create mode 100644 src/dal_select2_queryset_sequence/views.py create mode 100644 src/dal_select2_queryset_sequence/widgets.py create mode 100644 src/dal_select2_tagging/__init__.py create mode 100644 src/dal_select2_tagging/widgets.py create mode 100644 src/dal_select2_taggit/__init__.py create mode 100644 src/dal_select2_taggit/widgets.py create mode 100644 test_project/.coveragerc create mode 100644 test_project/custom_select2/__init__.py create mode 100644 test_project/custom_select2/admin.py create mode 100644 test_project/custom_select2/apps.py create mode 100644 test_project/custom_select2/forms.py create mode 100644 test_project/custom_select2/migrations/0001_initial.py create mode 100644 test_project/custom_select2/migrations/__init__.py create mode 100644 test_project/custom_select2/models.py create mode 100644 test_project/custom_select2/static/t_select2.js create mode 100644 test_project/custom_select2/test_functional.py create mode 100644 test_project/custom_select2/urls.py create mode 100644 test_project/custom_select2/widgets.py create mode 100644 test_project/forward_different_fields/__init__.py create mode 100644 test_project/forward_different_fields/admin.py create mode 100644 test_project/forward_different_fields/apps.py create mode 100644 test_project/forward_different_fields/forms.py create mode 100644 test_project/forward_different_fields/migrations/0001_initial.py create mode 100644 test_project/forward_different_fields/migrations/__init__.py create mode 100644 test_project/forward_different_fields/models.py create mode 100644 test_project/forward_different_fields/static/js_handlers.js create mode 100644 test_project/forward_different_fields/tests.py create mode 100644 test_project/forward_different_fields/urls.py create mode 100644 test_project/linked_data/__init__.py create mode 100644 test_project/linked_data/admin.py create mode 100644 test_project/linked_data/apps.py create mode 100644 test_project/linked_data/forms.py create mode 100644 test_project/linked_data/migrations/0001_initial.py create mode 100644 test_project/linked_data/migrations/__init__.py create mode 100644 test_project/linked_data/models.py create mode 100644 test_project/linked_data/static/linked_data.js create mode 100644 test_project/linked_data/test_forms.py create mode 100644 test_project/linked_data/test_functional.py create mode 100644 test_project/linked_data/test_views.py create mode 100644 test_project/linked_data/urls.py create mode 100755 test_project/manage.py create mode 100644 test_project/rename_forward/__init__.py create mode 100644 test_project/rename_forward/admin.py create mode 100644 test_project/rename_forward/apps.py create mode 100644 test_project/rename_forward/forms.py create mode 100644 test_project/rename_forward/migrations/0001_initial.py create mode 100644 test_project/rename_forward/migrations/__init__.py create mode 100644 test_project/rename_forward/models.py create mode 100644 test_project/rename_forward/static/linked_data.js create mode 100644 test_project/rename_forward/test_functional.py create mode 100644 test_project/rename_forward/urls.py create mode 100644 test_project/requirements.txt create mode 100644 test_project/secure_data/__init__.py create mode 100644 test_project/secure_data/admin.py create mode 100644 test_project/secure_data/apps.py create mode 100644 test_project/secure_data/forms.py create mode 100644 test_project/secure_data/migrations/0001_initial.py create mode 100644 test_project/secure_data/migrations/__init__.py create mode 100644 test_project/secure_data/models.py create mode 100644 test_project/secure_data/test_functional.py create mode 100644 test_project/secure_data/urls.py create mode 100644 test_project/secure_data/views.py create mode 100644 test_project/select2_djhacker_formfield/__init__.py create mode 100644 test_project/select2_djhacker_formfield/admin.py create mode 100644 test_project/select2_djhacker_formfield/apps.py create mode 100644 test_project/select2_djhacker_formfield/migrations/0001_initial.py create mode 100644 test_project/select2_djhacker_formfield/migrations/__init__.py create mode 100644 test_project/select2_djhacker_formfield/models.py create mode 100644 test_project/select2_djhacker_formfield/test_functional.py create mode 100644 test_project/select2_djhacker_formfield/urls.py create mode 100644 test_project/select2_foreign_key/__init__.py create mode 100644 test_project/select2_foreign_key/admin.py create mode 100644 test_project/select2_foreign_key/apps.py create mode 100644 test_project/select2_foreign_key/forms.py create mode 100644 test_project/select2_foreign_key/migrations/0001_initial.py create mode 100644 test_project/select2_foreign_key/migrations/__init__.py create mode 100644 test_project/select2_foreign_key/models.py create mode 100644 test_project/select2_foreign_key/test_functional.py create mode 100644 test_project/select2_foreign_key/urls.py create mode 100644 test_project/select2_generic_foreign_key/__init__.py create mode 100644 test_project/select2_generic_foreign_key/admin.py create mode 100644 test_project/select2_generic_foreign_key/apps.py create mode 100644 test_project/select2_generic_foreign_key/forms.py create mode 100644 test_project/select2_generic_foreign_key/migrations/0001_initial.py create mode 100644 test_project/select2_generic_foreign_key/migrations/__init__.py create mode 100644 test_project/select2_generic_foreign_key/models.py create mode 100644 test_project/select2_generic_foreign_key/test_forms.py create mode 100644 test_project/select2_generic_foreign_key/test_functional.py create mode 100644 test_project/select2_generic_foreign_key/test_views.py create mode 100644 test_project/select2_generic_foreign_key/urls.py create mode 100644 test_project/select2_generic_foreign_key/views.py create mode 100644 test_project/select2_generic_m2m/__init__.py create mode 100644 test_project/select2_generic_m2m/admin.py create mode 100644 test_project/select2_generic_m2m/apps.py create mode 100644 test_project/select2_generic_m2m/forms.py create mode 100644 test_project/select2_generic_m2m/models.py create mode 100644 test_project/select2_generic_m2m/test_forms.py create mode 100644 test_project/select2_generic_m2m/test_functional.py create mode 100644 test_project/select2_generic_m2m/urls.py create mode 100644 test_project/select2_generic_m2m/views.py create mode 100644 test_project/select2_gm2m/__init__.py create mode 100644 test_project/select2_gm2m/admin.py create mode 100644 test_project/select2_gm2m/apps.py create mode 100644 test_project/select2_gm2m/forms.py create mode 100644 test_project/select2_gm2m/models.py create mode 100644 test_project/select2_gm2m/test_forms.py create mode 100644 test_project/select2_gm2m/test_functional.py create mode 100644 test_project/select2_gm2m/urls.py create mode 100644 test_project/select2_gm2m/views.py create mode 100644 test_project/select2_list/__init__.py create mode 100644 test_project/select2_list/admin.py create mode 100644 test_project/select2_list/forms.py create mode 100644 test_project/select2_list/migrations/0001_initial.py create mode 100644 test_project/select2_list/migrations/__init__.py create mode 100644 test_project/select2_list/models.py create mode 100644 test_project/select2_list/test_fields.py create mode 100644 test_project/select2_list/test_functional.py create mode 100644 test_project/select2_list/test_views.py create mode 100644 test_project/select2_list/urls.py create mode 100644 test_project/select2_list/views.py create mode 100644 test_project/select2_many_to_many/__init__.py create mode 100644 test_project/select2_many_to_many/admin.py create mode 100644 test_project/select2_many_to_many/apps.py create mode 100644 test_project/select2_many_to_many/forms.py create mode 100644 test_project/select2_many_to_many/migrations/0001_initial.py create mode 100644 test_project/select2_many_to_many/migrations/__init__.py create mode 100644 test_project/select2_many_to_many/models.py create mode 100644 test_project/select2_many_to_many/test_functional.py create mode 100644 test_project/select2_many_to_many/urls.py create mode 100644 test_project/select2_nestedadmin/__init__.py create mode 100644 test_project/select2_nestedadmin/admin.py create mode 100644 test_project/select2_nestedadmin/apps.py create mode 100644 test_project/select2_nestedadmin/forms.py create mode 100644 test_project/select2_nestedadmin/migrations/0001_initial.py create mode 100644 test_project/select2_nestedadmin/migrations/__init__.py create mode 100644 test_project/select2_nestedadmin/models.py create mode 100644 test_project/select2_nestedadmin/test_functional.py create mode 100644 test_project/select2_nestedadmin/urls.py create mode 100644 test_project/select2_one_to_one/__init__.py create mode 100644 test_project/select2_one_to_one/admin.py create mode 100644 test_project/select2_one_to_one/apps.py create mode 100644 test_project/select2_one_to_one/forms.py create mode 100644 test_project/select2_one_to_one/migrations/0001_initial.py create mode 100644 test_project/select2_one_to_one/migrations/__init__.py create mode 100644 test_project/select2_one_to_one/models.py create mode 100644 test_project/select2_one_to_one/test_functional.py create mode 100644 test_project/select2_one_to_one/urls.py create mode 100644 test_project/select2_outside_admin/__init__.py create mode 100644 test_project/select2_outside_admin/templates/select2_outside_admin.html create mode 100644 test_project/select2_outside_admin/urls.py create mode 100644 test_project/select2_outside_admin/views.py create mode 100644 test_project/select2_tagging/__init__.py create mode 100644 test_project/select2_tagging/admin.py create mode 100644 test_project/select2_tagging/forms.py create mode 100644 test_project/select2_tagging/models.py create mode 100644 test_project/select2_tagging/test_forms.py create mode 100644 test_project/select2_tagging/test_functional.py create mode 100644 test_project/select2_tagging/urls.py create mode 100644 test_project/select2_taggit/__init__.py create mode 100644 test_project/select2_taggit/admin.py create mode 100644 test_project/select2_taggit/forms.py create mode 100644 test_project/select2_taggit/migrations/0001_initial.py create mode 100644 test_project/select2_taggit/migrations/__init__.py create mode 100644 test_project/select2_taggit/models.py create mode 100644 test_project/select2_taggit/test_forms.py create mode 100644 test_project/select2_taggit/test_functional.py create mode 100644 test_project/select2_taggit/urls.py create mode 100644 test_project/settings/__init__.py create mode 100644 test_project/settings/base.py create mode 100644 test_project/templates/admin/login.html create mode 100644 test_project/templates/base.html create mode 100644 test_project/tests/__init__.py create mode 100644 test_project/tests/admin.py create mode 100644 test_project/tests/models.py create mode 100644 test_project/tests/test_widgets.py create mode 100644 test_project/urls.py create mode 100644 test_project/views.py create mode 100644 test_project/wsgi.py create mode 100644 tox.ini diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..95e454815 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,42 @@ +name: Clean release +run-name: ${{ github.actor }} release +on: + push: + tags: + - '*' +jobs: + pypi-release: + runs-on: ubuntu-latest + container: + image: yourlabs/python + steps: + - name: Check out repository code + uses: actions/checkout@v3 + - name: debug this crazy environment + run: | + chown -R app:app . + su - app -c "cd $(pwd) && npm install && npm run build" + - name: Update version in setup.py and docs/conf.py + run: | + short=$(echo ${GITHUB_REF##*/} | grep -Eo '[^.]+\.[^.]+') + sed -i "s/version=[^,]*,/version='${GITHUB_REF##*/}',/" setup.py + sed -i "s/release = [^,]*,/release = '${GITHUB_REF##*/}'/" docs/conf.py + sed -i 's/version": "[^"]*"/version": "$short"/' package.json + sed -i "s/version = [^,]*,/version = '${GITHUB_REF##*/}'/" docs/conf.py + - name: Update changelog + run: echo -e "$(python changelog.py ${GITHUB_REF##*/})\n$(cat CHANGELOG)" > CHANGELOG + - name: Fix git dubious ownership + run: git config --global --add safe.directory /__w/django-autocomplete-light/django-autocomplete-light + - name: Commit all generated files + uses: stefanzweifel/git-auto-commit-action@v4 + with: + commit_options: '--amend --no-edit' + branch: master + push_options: '--force' + - name: Build python package + run: python setup.py sdist + - name: Twine upload + env: + TWINE_USERNAME: __token__ + TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }} + run: twine upload dist/django-autocomplete-light-${GITHUB_REF##*/}.tar.gz diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..7e70e6a0d --- /dev/null +++ b/.gitignore @@ -0,0 +1,20 @@ +*pyc +docs/build +docs/source/_static +test_project/htmlcov/ +test_project/.coverage +build +dist +django_autocomplete_light.egg-info/ +*swp +docs/docs +__pycache__ +test_env/ +.coverage +.tox +node_modules + +.cache/ + +# Pycharm +.idea/ diff --git a/.openshift/README.md b/.openshift/README.md new file mode 100644 index 000000000..785566bc0 --- /dev/null +++ b/.openshift/README.md @@ -0,0 +1,5 @@ +The OpenShift `python` cartridge documentation can be found at: +http://openshift.github.io/documentation/oo_cartridge_guide.html#python + +For information about .openshift directory, consult the documentation: +http://openshift.github.io/documentation/oo_user_guide.html#the-openshift-directory diff --git a/.openshift/action_hooks/README.md b/.openshift/action_hooks/README.md new file mode 100644 index 000000000..541319581 --- /dev/null +++ b/.openshift/action_hooks/README.md @@ -0,0 +1,3 @@ +For information about action hooks, consult the documentation: + +http://openshift.github.io/documentation/oo_user_guide.html#action-hooks diff --git a/.openshift/action_hooks/deploy b/.openshift/action_hooks/deploy new file mode 100755 index 000000000..c8838a0db --- /dev/null +++ b/.openshift/action_hooks/deploy @@ -0,0 +1,27 @@ +#!/bin/bash +# This deploy hook gets executed after dependencies are resolved and the +# build hook has been run but before the application has been started back +# up again. This script gets executed directly, so it could be python, php, +# ruby, etc. +set -xe + +source ${OPENSHIFT_HOMEDIR}app-root/runtime/dependencies/python/virtenv/bin/activate + +pip install -U pip + +pip install -r ${OPENSHIFT_REPO_DIR}test_project/requirements.txt + +# Broken dep pulled in by taggit +if pip freeze | grep south; then + pip uninstall -y south +fi + +pushd ${OPENSHIFT_REPO_DIR}test_project +./manage.py migrate --fake-initial --noinput +mkdir -p wsgi +./manage.py collectstatic --noinput +popd + +mkdir -p ${OPENSHIFT_DATA_DIR}media +mkdir -p ${OPENSHIFT_REPO_DIR}wsgi/static/media +ln -sf ${OPENSHIFT_DATA_DIR}media ${OPENSHIFT_REPO_DIR}wsgi/static/media diff --git a/.openshift/cron/README.cron b/.openshift/cron/README.cron new file mode 100644 index 000000000..ac77f7872 --- /dev/null +++ b/.openshift/cron/README.cron @@ -0,0 +1,27 @@ +Run scripts or jobs on a periodic basis +======================================= +Any scripts or jobs added to the minutely, hourly, daily, weekly or monthly +directories will be run on a scheduled basis (frequency is as indicated by the +name of the directory) using run-parts. + +run-parts ignores any files that are hidden or dotfiles (.*) or backup +files (*~ or *,) or named *.{rpmsave,rpmorig,rpmnew,swp,cfsaved} + +The presence of two specially named files jobs.deny and jobs.allow controls +how run-parts executes your scripts/jobs. + jobs.deny ===> Prevents specific scripts or jobs from being executed. + jobs.allow ===> Only execute the named scripts or jobs (all other/non-named + scripts that exist in this directory are ignored). + +The principles of jobs.deny and jobs.allow are the same as those of cron.deny +and cron.allow and are described in detail at: + http://docs.redhat.com/docs/en-US/Red_Hat_Enterprise_Linux/6/html/Deployment_Guide/ch-Automating_System_Tasks.html#s2-autotasks-cron-access + +See: man crontab or above link for more details and see the the weekly/ + directory for an example. + +PLEASE NOTE: The Cron cartridge must be installed in order to run the configured jobs. + +For more information about cron, consult the documentation: +http://openshift.github.io/documentation/oo_cartridge_guide.html#cron +http://openshift.github.io/documentation/oo_user_guide.html#cron diff --git a/.openshift/cron/hourly/reset_database b/.openshift/cron/hourly/reset_database new file mode 100755 index 000000000..de96a51e2 --- /dev/null +++ b/.openshift/cron/hourly/reset_database @@ -0,0 +1,9 @@ +#!/bin/bash +set -x + +source ${OPENSHIFT_HOMEDIR}app-root/runtime/dependencies/python/virtenv/bin/activate + +pushd ${OPENSHIFT_REPO_DIR}test_project +rm -rf db.sqlite +./manage.py migrate --noinput +popd diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 000000000..221ce2196 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,23 @@ +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Set the version of Python and other tools you might need +build: + os: ubuntu-20.04 + tools: + python: "3.9" + +# Build documentation in the docs/ directory with Sphinx +sphinx: + configuration: docs/conf.py + +# If using Sphinx, optionally build your docs in additional formats such as PDF +# formats: +# - pdf + +# Optionally declare the Python requirements required to build your docs +python: + install: + - requirements: docs/requirements.txt diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..c082c6566 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,65 @@ +sudo: false +dist: xenial +language: python +env: + global: + - PIP_RETRIES=10 + - PIP_TIMEOUT=30 + - BROWSER=firefox + - GECKODRIVER=0.24.0 + - MOZ_HEADLESS=1 +matrix: + include: + - python: 3.7 + env: TOXENV=checkqa + - python: 3.7 + env: TOXENV=docs + - python: 3.6 + env: TOXENV=py36-dj32 + - python: 3.7 + env: TOXENV=py37-dj32 + - python: 3.8 + env: TOXENV=py38-dj40 + - python: 3.8 + env: TOXENV=py38-dj41 + - python: 3.9 + env: TOXENV=py39-dj41 + - python: 3.10 + env: TOXENV=py310-dj41 + - python: 3.8 + env: TOXENV=py38-dj42 + - python: 3.9 + env: TOXENV=py39-dj42 + - python: 3.10 + env: TOXENV=py310-dj42 +install: +- travis_retry pip install -U pip +- travis_retry pip install tox +- travis_retry pip freeze +services: + - firefox: latest +before_install: + - wget https://github.com/mozilla/geckodriver/releases/download/v$GECKODRIVER/geckodriver-v$GECKODRIVER-linux64.tar.gz + - mkdir -p geckodriver && tar -xzf geckodriver-v$GECKODRIVER-linux64.tar.gz -C geckodriver + - export PATH=$(pwd)/geckodriver:$PATH +before_script: +- if echo "$TOXENV" | grep mysql; then mysql -e 'create database autocomplete_light_test;'; + fi +- if echo "$TOXENV" | grep postgresql; then psql -c 'create database autocomplete_light_test;' + -U postgres; fi +script: +- tox -r +- test -d .tox/$TOXENV/log && cat .tox/$TOXENV/log/*.log || true +after_success: +- travis_retry pip install codecov +- cd test_project && codecov +notifications: + irc: + channels: + - irc.freenode.org#yourlabs + template: + - "%{repository} (%{commit} %{author}) : %{message} %{build_url} %{compare_url}" +cache: + directories: + - .tox/$TOXENV + - $HOME/.cache/pip diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 000000000..ae1ffbb17 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,90 @@ +The following aims at being a complete list of coders who contributed code or +documentation to the project. + +- Aaron VanDerlip +- Adam Dobrawy +- Aidan Lister +- Alan Justino da Silva +- Alessandro Dentella +- Alexandr Artemyev +- Andreas Madsack +- Andrew Plummer +- Andrey @onrik +- Andy Baker +- Andy Dustman +- Antoine Pinsard +- Árni St. Sigurðsson +- Ask Holme +- Bachurin Sergey +- balmaster +- benjaoming +- Berg @bergsoft +- Bernhard Vallant +- Brad Buran +- Brant.Y +- Bruno Alla +- Daniele Procida +- Daniel Hahler @blueyed +- David Aurelio +- David Sanders +- Derek Stegelman +- Dmitri Bogomolov +- Ewoud Kohl van Wijngaarden +- Fábio C. Barrionuevo da Luz +- Fidel Ramos +- Florentin Hennecker +- Gabriel Rodríguez Alberich +- Giorgos Logiotatidis +- Helen Sherwood-Taylor +- Ian Leith +- Igor Mitrenko +- Italo Maia +- Ivan Metzlar +- James Pic +- Jonas Haag +- Jonathan Dorival +- Jonatha Wiklund +- Jubing Chen +- kakulukia +- Tatiana Krikun +- Luke Plant +- MadEng84 +- Marcelo Jorge Vieira +- Marc Hoersken +- Mariano Bianchi +- Mario César Señoranis Ayala +- Michael Lin +- Michał Pasternak +- Michał Sałaban +- Mike Covington +- Mislav Cimpersak +- Mounir Messelmeni +- Maxim @mpyatishev +- Nguyễn Hồng Quân +- Nicolas Allemand +- Nick Catalano +- Odin Hørthe Omdal +- @pandabuilder +- Patrick Taylor +- Philip Mertens +- Piet Delport +- Riccardo Magliocchetti +- Raphael Kimmig +- Robert Rollins +- Roger Hunwicks +- Ruurd Moelker +- Ryan P Kilby +- SaeX +- sbaum +- SebCorbin +- Seinlet Nicolas +- Simon Kohlmeyer +- Simon Meers +- Steve Ellis +- Theo Hennessy +- Thijs Triemstra +- Tom de Simone +- Tomas Peterka +- Tyler Kellogg +- Visgean Skeloru +- Volodymyr Tartynskyi diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 000000000..cae60c869 --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,1300 @@ + +3.9.5-rc6 + + 2023-04-07 Release 3.9.5-rc6 + dea11bf changelog.py: fix release dates and tag commits by jpic + +3.9.5-rc5 + + 2023-04-07 Release 3.9.5-rc5 + +3.9.5-rc4 + + 2023-04-07 Release 3.9.5-rc4 + 7a45b75 Support local commits in changelog.py by jpic + +3.9.5-rc3 + + 2023-04-07 Release 3.9.5-rc3 + 0cd3b7b Support releases without rebuild by jpic + e52a25a Support release commit in changelog script by jpic + +3.9.5-rc2 + + 2023-04-07 Release 3.9.5-rc2 + 0ba6259 Rebuild static by jpic + b463af0 Fix #1295: Automate CHANGELOG by jpic + #1321 catch ValueError if selected_choices are invalid by @shapiromatron + 62ecb5f QA by jpic + bde0029 Test create option validation by jpic + 5ac5fb1 Create option on the fly test fix by jpic + #1314 fix: test_project/requirements.txt to reduce vulnerabilities by @jpic + #1310 Use get_result_value when returning created object by @matevz-ap + #1308 Adding `.select2-container--default` class to all style rules so that other themes aren't affected by @trumpet2012 + #1308 Updating light mode to use original highlighted row colors by @trumpet2012 + #1308 Adding support for django css variables so that for both light and dark themed admins the select2 fields will look good. by @trumpet2012 + #1309 Fixing issue where dragging an inline admin section would initialize the autocomplete fields during the dragging view and when dragging stopped the autocomplete field would be reset and not get initialized again since the internal initialized list would already contain that element. by @trumpet2012 + 8bb371f Fix release script by jpic + +3.9.5-rc1 + + 2022-09-09 Release 3.9.5-rc1 + c1eaa20 Rebuild static by jpic + #1293 Add the option to validate the field when creating a new choice by @etiennepouliot + ac7860f Fix TOC sidebar with furo theme by jpic + #1306 Test Django 4.1 by @adamchainz + 3599b6f Move documentation theme to furo by jpic + 2022-09-09 Release 3.9.5-rc0 + #1307 QA by @jpic + #1307 Support Python 3.10 and Django 4 by @jpic + #1307 Rebuild static by @jpic + #1304 docs: Fix a few typos by @timgates42 + #1302 finnish translations by @T-101 + #1297 Add reminder to install django-querysetsequence by @tylerecouture + +3.9.4 + + 2022-03-15 Release 3.9.4 + 98ab8be Fix import by jpic + +3.9.3 + + 2022-03-15 Release 3.9.3 + 77beaad Python 3.10 support by jpic + +3.9.2 + + 2022-03-14 Release 3.9.2 + #1289 The assumption that sys.argv has a second element is flawed by @bernd-wechner + #1286 add GitHub URL for PyPi by @andriyor + #1287 Fixes in response to requests by @bernd-wechner + #1287 Fixed JQuery version so the test works! by @bernd-wechner + #1287 Two isolated unadorned simple test cases added by @bernd-wechner + 4adff74 Fix #1237: metadata versions by jpic + +3.9.1 + + 2022-02-03 Release 3.9.1 + +3.9.1rc2 + + 2022-01-27 Release 3.9.1rc2 + +3.9.1rc1 + + 2022-01-27 Release 3.9.1rc1 + 1e4d83b Fix release script by jpic + #1280 Selected field's text color changed by @ammarCanc + +3.9.0 + + 2022-01-27 Release 3.9.0 + #1268 Django 4.0 test by @jpic + 3eb84a0 Improve tutorial, remove form field override by jpic + c40e5dc Test project settings cleaning by jpic + 1347911 Fix linked_data.js example by jpic + f4013bb Support None choices by jpic + +3.9.0rc5 + + 2021-11-26 Release 3.9.0rc5 + 72b8900 Fix #1140: Warn about a django admin bug by jpic + 8de5b22 Rewrite Select2ListField by jpic + d57a87b Remove db.sqlite3 binary by jpic + 467670e Changelog update by jpic + +3.9.0rc4 + + NEW FEATURE FOR EVERYONE! Read here and njoy :) + https://github.com/yourlabs/django-autocomplete-light/blob/master/docs/tutorial.rst#with-djhacker + + #1270 Fix issue when dal js is loaded twice by @FiooCode + #1266 New data-token-separator attribute by @fraimondo + #1190 #1187 Fixed django-querysetsequence support + Django 3.2 support in test code + +3.9.0rc3 failed release + +3.9.0-rc2 + + #1262 Django 4.0 support by @fraimondo + #1255 Django 4.0 warning removal by @Mogost + #1253 Case sensitive creates in Select2QuerySetView by @shenek + #1246 Add event to notify of the initialization completed by @andreccorrea + Select2 Upgraded to 4.1.0-rc.0 + +3.9.0-rc1 + + #1222 Select2GroupQuerySetView fix by @arseniy-panfilov + #1233 Safari regexp fix by @vtbassmatt + #1239 Django 3.2+ fix by @michael-sayapin + #1251 Doc fix get_result_label by @arlopezg + #1231 Fix ViewMixin comment by @moonorange + +3.8.2 Upgrade to select2 4.0.13 + +3.8.1 BUGFIX RELEASE + + Fix #1175: Rebuild js + +3.8.0 + + A LOT of fixes, but they come at a price ... + + BACKWARD COMPATIBILITY BREAK: + You now MUST register your custom init functions as per + https://django-autocomplete-light.readthedocs.io/en/master/tutorial.html#overriding-javascript-code + + Changes: + + #1171 data-html and object creation bugfix by @syserr0r + #1182 fix production with select2 upgrade by @MarkRunWu + #1169 implement search_fields and split_words by @MuckRock + #1145 fix post method parameters by @erdnax123 + #1162 JS load order issues by @danielmorell + #1157 jQuery loading by @danielmorell + #1162 fix by @danielmorell this one introduces the breaking changes + #1129 fix rtfd autobuild by @jpic + #1158 remove calculated width by @nad2000 + #1159 Fix UUID for GFK models by @sayeghr + #1138 Replace DOMNodeInserted with MutationObserver by @CristopherH95 + #1144 doc typo fix by @timgates42 + Flake8, Tox, Travis CI, tests ... fix by @jpic + +3.7.0 & 3.6.0 + + Well, Pypi won't let me upload there, it says: + + HTTPError: 400 Client Error: This filename has already been used, use a different version. See https://pypi.org/help/#file-name-reuse for more information. for url: https://upload.pypi.org/legacy/ + + But I only see 3.6.0.dev0 and 3.6.0.dev1 and 3.7.0.dev0, I suppose I should + have done pre-releases on 3.6.dev0 and 3.7.dev0. + + Anyway, it lets me upload on 3.8.0, one of the last 3.x releases ! + + But don't worry, 4.0 will not break anything, it will merely add a new + autocomplete script for people who want out of select2 or want something + more like what we had in DAL 1 & 2, it's here: + + Demo: https://oss.yourlabs.me/autocomplete-light/ + Source: https://yourlabs.io/oss/autocomplete-light + + For those who remember, this solves *exactly* the same problems that + jquery-autocomplete-light did, except it's a lightweight StencilJS + WebComponent now. + +3.6.0 + + This release actually never hit Pypi. + +3.5.1 + + jquery.js was still loaded by the widget, fixed by @timthelion + +3.5.0 + + BACKWARD COMPATIBILITY BREAKS: + - jquery.js has been **removed** from widget.media, this means that you are + now responsible for ensuring that jquery is loaded in your page prior to + displaying the form (form.media). + - trailing dash was replaced by underscore in forward conf, ie. + dal-forward-conf-for-id_test becomes dal-forward-conf-for_id_test + + #1115: Compatibility with Django 3.0 by Alexandr Artemyev @mogost + #1079: Fixed access to $.fn.select2 by David @dwasyl + #1118: Highlight select field with error to match Django style by @tchatow + #1099: django-nested-admin forwarded field fix by @akshenc + + Also, tests now run fine on Travis thanks to @jorrit-wehelp + +3.4.1 Fix #1108: i18n path (thanks @philgyford for report) + +3.4.0 Python 2 and Django < 2.0 support + +3.3.4 re-release without dirty source + +3.3.3 django < 2.0 support with dal_static contributed by @andybak + +3.3.2 django.contrib.admin fix static files by @coredumperror + +3.3.1 + + - Fixed a bug in the way jquery.init.js was being used by @coredumperror + - Set select2 container CSS class to :all: @hbielenia + - Added missing renderer parameter to render method for django 2.1 @monim67 + - Fix ImportError with SELECT2_TRANSLATIONS in Django 1.x @hugorodgerbrown + - Forward argument should always be a tuple @jihoon796 + - Fixed exception thrown from Select2QuerySEtView when paginate_by is set + @coredumperror + +3.3.0 + + - use admin statics + - #981: create option behaviour + - #995: automatically generated views for generic foreign key fields + - Getting placeholder and minimumInputLength from dal select + - #1017: Initial migrations and database + - Turkish translation + - Added support for forwarded fields to Select2GenericForeignKeyModelField + +3.3.0-rc6 + + #959 + +3.3.0-rc5 + + #895: Self() and JavaScript() forward features + +3.3.0-rc4 + + #843: Forward logic refactored. Specifications for types of forwarded values. + +3.3.0-rc3 + + #957 remove reference to deleted script (rebase issue introduced in + 3.3.0-rc1) + +3.3.0-rc2 + + Revert 5b37f8661, fixes tests. + +3.3.0-rc1 + +This version supports Django 2.0 and Python 3.6, perhaps more but I have not +tested, please submit compatibility patches for older versions if needed. +Please test them with tox -e base-py36-django20-sqlite before pushing. + +To install 3.3.0-rc1, use `pip install django-autocomplete-light==3.3.0-rc1`. + +New features: + + #953: Select2 update to 3.4.0.6-rc.1 by @jpic + #917: django-nested-admin support by @loicteixeira + #815: Simplify customization of autocomplete views by @EvaSDK + #746: Select2 Language and dynamic Media by @luzfcb + #883: Allow overwriting the results by @eayin2 + +Bug fixes: + + #874: Fix Django 1.11.3 error by @ikcam + #933: Python 3.6 and Django 2.0 support by @jpic + #930: QuerySetSequence querysets order is not preserved by @melvyn-sopacua + #909: Prevent initilization of other selects by @loicteixeira + #904: Fix KeyError when id is not in attrs by @dwheaton + #885: Prevent rendering of empty option on multi select by @johandc + #892: Enable different item label for selected item by @maximpetrov + #926: Atomic create_object by @jpic + #718: Remove temp hack for select2 by @FuzzAU + #860: dal: widgets: use the name if we don't have the id by @xrmx + #849: Don't create a new option if an iexact-matching one already exists by @liwenyip + +Also thanks to the many documentation contributors. + + #874: Fix Django 1.11.3 error by @ikcam + #937: Update tutorial.rst to fix XSS in the example by @hangtwenty + #919: Better create new object example by @davideghz + #928: Add note about slim jqueries by @melvyn-sopacua + +Test notes: + + I have not tested this release with other Python and Django versions, + and also tests don't pass on travis despite the effort. It's working + on all browsers here and i've chased many seleniumish race conditions + but it's not enough for travis. + So, there's no docker image available with python and selenium that + looks good i'm probably going to make one at some point but it's not + today's priority as far as I'm concerned. + So, tests are run locally which means manual action, but i've left the QA + checks on travis as mandatory because i've fixed so many PEP8 mistakes + during this release ... + +Congratulations for this release my friends, because a lot of great work has +been contributed by the community since last release 4 months ago. + +3.2.10 + + #877: Return proper content type for json by @mpasternak + #819: Fixed JS autocomplete initializer by @apinsard + #871: Add many translations by @dima-kov + #879: Add classifiers in setup.py by @ad-m + #868: Fix selector used to allow clearing non-required fields. + #861: prefer pristine jQuery to the django one by @xrmx + +3.2.9 + + HEAD is now at a44a2ed Fixed JS autocomplete initializer (#819) (#820) by + @apinsard + +3.2.8 + + #823: Optgroup list support with Select2GroupListView by @jsurloppe + #841: Allow boolean "data-html" attribute for widget on python side by @gagarski + #839: Support for forwarding radio buttons properly by @gagarski + #839: Add checkbox handling in forwards by @marekjedrzejewski + #833: Add functional tests for rename_forward app by @gagarski + +3.2.7 + + Remove forward.js from Select2WidgetMixin.Media by @gagarski + #838: Use namespaced jQuery in `get_forwards` by @ryan-copperleaf + #836: Queryset sequence view to display actual model name by @jsoa + 3.2.5 and 3.2.6 were removed from PyPi but are the same. + +3.2.4 + + #813: Return 400 on invalid input by @EvaSDK + +3.2.3 Two seriously good community contributed bugfixes + + #799: Support serializing UUIDs and add tests for models with UUIDs as PKs + by @blag + #826: Prevent rendering of empty option on multi select + by @beruic + +3.2.2 Test fixes, Django 1.10 and 1.11 + +3.2.1 + + #737: TaggitSelect2: insure there's a comma when there's only one tag, or + tag "Multi word" would end up as "Multi" and "word" by + @Ixxy-Open-Source + #743: Fix placeholder not working when URL is not given to autocomplete + widget (#743) by @thauk-copperleaf + #756: Forward Capabilities Outside Admin by @gagarski + +3.2.0 + + #745: Add list autocomplete by @dmosberger, @jpic and @thecardcheat + #754: dal_queryset_sequence documentation update by @chubz + #734: Move create_option functionality into it's own method @andybak + #741: Fix initial value not set when selected choices aren't strings + @thauk-copperleaf + #733: Advanced forward features @gagarski + #752: Add doc test to CI by @jpic + #730: Resolve SystemCheckError fields.E304 @thecardcheat + #721: Note about placement of DAL before grappelli by @chubz + #748: Update docs example about loading jquery outside the admin @jpic + + Mid version number bumped as a tribute to the new major features that were + added, however, there is no know backward compatibility break from 3.1 to + 3.2. + +3.1.8 #727 Add missing static files + +3.1.7 + + #714: Update select2 by @meesterguyman + #705: Improve compat with dj1.9 by @9nix00 + #706: Fix extra require by @blueyed + #710: Added note for static files not checked by @geekashu + #708: Provide path with dal_select2.E001 by @blueyed + #700: Enable HTML markup in select2 item labels by @njoyard + +3.1.6 + + #671: Create_field support with querysetsequence + #679: Allow create message translation by @maisim + #682: Extend 'forward' attribute to creating objects as well by @toudi + #666: Improved missing 'created_field' error by @guettli + #670: Javascript loading documentation + #678: Added example to update autocompletes in JS + +3.1.5 + + #661: Field forwarding: support form prefix, and formsets at the same time. + #628: Improve option rename hook used until the patch is merged upstream in + select2. + #656: Use http.JsonResponse() to return json by @guettli + #638: System check for dal_select2 to raise an error if select2 is not + available. + #662: Examples for setting placeholder and minimum input length options in + select2. + #619: From django-sbo-selenium to pytest+splinter. + +3.1.4 #646: Released way too much node_modules in select2, sorry ! + +3.1.3 #640: Bugfix: Taggit select2 widget in invalid forms + +3.1.2 + + - #634: Select2 Upgrade + - #628: Do not rely on ids to fix container text + - #631: Initializing GFKs with values from forms + - #623: Fix setup.py extras by @ticosax + - #610: Cancel out Django's style on lists + +3.1.1 Forgot vendor files in 3.1.0 + +3.1.0 + + - ! BC BREAK ! CreateModelField is gone, it didn't work when another field + of the form didn't validate. There isn't a really sane way to create the + option in the form field field. Instead, this is done in the view, like + in DAL v2. + - ! BC BREAK ! dal_tagulous was removed in favor of django-tagging, because + tagulous already provides its implementation of a select2 widget. + - #610 Fixed a rendering issue with multiple selects. Upstream patch + proposed but not merged yet: select2/select2#4226 + + For details about the current status and plan, please consult the + announcement: + + http://blog.yourlabs.org/post/140477620808/django-autocomplete-light-v3-whats-going-on + +3.0.4 #586 Prune select2 docs by @eraldo + +3.0.3 #586 Include all in MANIFEST by @eraldo + +3.0.2 #575 Prevent Django admin from setting up jQuery with noConflict + +3.0.1 #573 Updated MANIFEST to include static files. + +3.0.0 Rewrite: easier to use, test, maintain and support. + +2.3.3 + + - #563 Don't disable multiplechoicewidgets if select is [multiple] + - Don't import anything in __init__ anymore if on django 1.9 + - test_project fixes, for the above. + +2.3.2 JAL#20 Don't disable multiplechoicewidgets by @JaHicks + +2.3.1 Forgot the static files in 2.3.0. + +2.3.0 + + - #555 Django 1.4, 1.5 and 1.6 deprecation + - #497 Enable registration by model-name by @luzfcb + - #536 #551 Support proxy models by @onrik + - #553 improved jQuery integration by @blueyed + - #516 Corrected french transation by @apinsard + - #541 Use error_messages on FieldBase to allow overrides by @dsanders11 + - #505 Ordering alias clash fix by @sandroden + - #515 Polish translation update by @mpasternak + - #543 ModelChoiceField requires the queryset argument + - #494 ModelChoiceField Watch changes to 'queryset' by @jonashaag + - #514 Fixed deprecation warning on Django 1.8 by @spookylukey + - #498 #548 improved i18n support + - #547 prevents loading genericm2m if not in INSTALLED_APPS + - JAL#18 Fix: Get value.length while value is null by @hongquan + - JAL#19 Clarify license by @stevellis, all MIT + - JAL#17 Disable the widget input when it is not in use @dsanders11 + - JAL#15 Support openning results in new tab @thebao + - JAL#14 Don't autohilight first choice by default @pandabuilder + - JAL#13 Add option for box aligning with right edge of input @dsanders11 + +IMPORTANT + +#536 At this point, proxy model support is untested, this is because I intend +to refactor the test suite and documentation during the 2.3.x serie. + +#494 Updating the queryset from outside the autocomplete class may lead to a +security problem, ie. if you don't replicate filters you apply manually on the +autocomplete object choices into choices_for_request() then a malicious user +could see choices which they shouldn't by querying the autocomplete directly. +3.0.0 will have automatic widget and / or field (in cases like gfk/gm2m) +registration to the view like in django-select2 using queryset.query caching +which would resolve that use-case. + +It's a pretty big release and I waited a while because I wanted all +contributors to benefit from it and work on the same codebase instead of +privileging one to another, not sure if it was the best way, let me know ! + +2.2.9, 2.2.10 + + [JAL] #12: Use exisiting input value for initial autocomplete query + #503: Fix Django 1.9 import error. + 2.2.9 release has not the updated AUTHORS and lacks note about #503 in + CHANGELOG, re-uploading as 2.2.10. + +2.2.8 + + #478, #472: "change related button" isn't clickable when it shouldn't. + +2.2.7 + + yourlabs/jquery-autocomplete-light#11: jqXHR.abort considered harmful, + @dsanders11 fixes BrokenPipe. + yourlabs/jquery-autocomplete-light#10: consistency fix: use $ instead + of jQuery for new functions by @dsanders11 + yourlabs/jquery-autocomplete-light#9: New parameter to disable + auto-hilighting on first choice #9 by @superzazu, should fix #495 too. + #477: Widget's render should use attrs even when created wthing __init__ by + @sandroden. + +2.2.6 + + jquery-autocomplete-light/#8: Positionning bug fix by @marianobianchi, + jquery-autocomplete-light/#6: Bugfix: Firefox won't hide input after + reload + when option was selected by @marianobianchi, + #483: German translation by Mounir Messelmeni + #476, #474, #482, documentation improvements by Mounir, Visgean Skeloru and + Florentin Hennecker. + +2.2.5 + + #452 Fix the case where there is a cap in the pg table or column name + #454: AutocompleteModel.order_by being a list would crash. + +2.2.4 + + #463 Update jquery-autocomplete-light which contains IE8 fixes. + yourlabs/jquery-autocomplete-light#4 by @RaphaelKimmig. + +2.2.3 + + #447: Ambiguous column names strikes again. This time we're using the + .column field attribute and covered a couple of cases including model + inheritance. + yourlabs/jquery-autocomplete-light#4 fixed clearInputOnSelectChoice + behaviour by @RaphaelKimmig: + + - by clearing autocomplete.value as well as input.val we make sure that + subsequent clicks will not open the autocomplete filtered by a value that + is no longer visible to the user + - by clearing the input before triggering the focus we make sure the + minimumCharacters settings is honored + +2.2.2 + + #422: Ambiguous column names in AutocompleteModelBase. + Also removed a generic exception expect/pass. + +2.2.1 + + Django 1.8 admin support hacks broke create_choices_on_the_fly example and + probably some other wicked JS things in user projects. + + Note that we have also extracted django admin specific hooks from widget.js + into django_admin.js. So if you have custom JS loading in the admin instead + of `{% include 'autocomplete_light/static.html' %}` then you should update + it to include django_admin.js in django admin. + +2.2.0 + + - #318: Remove extra spaces rendered in choices. + - #438: Hide autocomplete on scroll in Firefox because it bugs (temp fix). + - #432: New bootstrap_modal test_app by @lucky-user. + - #118: Extracted JS into a standalone jquery-plugin: + https://github.com/yourlabs/jquery-autocomplete-light + +2.2.0rc8 + + - #408: Django admin's new edit link was implemented in such a way that + it's unable to generate itself properly server-side on initial rendering. + +2.2.0rc7 + + - call fixPosition() on window resize if the autocomplete box is shown. + +2.2.0rc6 + + - #431 strikes again, default to body when it can't find a relevant + container. + +2.2.0rc5 + + - Reduce default latency because hardware is better. + - #431: Dynamic evaluation of the autocomp box container. + +2.2.0rc4 + + @lucky-user contributed Opera/Chromium improved support. + +2.2.0rc3 + + - #429: handle required=True in form fields again, regression introduced + in rc1's python BC break. + - #426: handle z-index since we're absolutely-positioning in 2.2.x + +Thanks a lot to the rc tester community + +2.2.0rc2 + + - #425: fixed blunt JS errors. + - Do not add pip debug log (~/.cache/pip/log/debug.log) to Travis cache. + - Just install 'pip>7', potentially using an updated version on Travis. + - Use travis_retry with "pip install", and also for `npm install`. + +2.2.0rc1 + + PENDING BREAK WARNING, Django >= 1.9. + + The good old ``import autocomplete_light`` API support will be dropped with + Django 1.9. All imports have moved to ``autocomplete_light.shortcuts`` and + importing ``autocomplete_light`` will work until the project is used with + Django 1.9. + + To be forward compatible with Django master (>=1.9) support, replace:: + + import autocomplete_light + + By: + + from autocomplete_light import shortcuts as al + + This will also make your scripts a lot shorter. + + CSS BREAK + + We've moved back to pre-1.1.10 CSS positioning. This means appending the + autocomplete box to an arbitrary DOM element (body by default) and using + calculating the ``top`` and ``bottom`` attribute in javascript with + ``yourlabs.Autocomplete.fixPosition()`` pretty much like Django admin's + calendar widget does. While blunt, this change should help the widget being + more compatible across Django admin themes. + + While this positioning system has been used since around 2005 in Django + when Adrian Holovaty open sourced admin media in commit dd5320d, it has + never been documented that's it's a good system that works well and there's + no reason to break backward compatibility in Django admin for that + - note to Django admin template customizers. + + JS BREAK + + Javascript ``yourlabs.Autocomplete`` object does not bind to the same + events as it used too. Event handling has been backported from twitter + typeahead and tested on firefox and android (#411). + + PYTHON BREAK + + The form field doesn't call ``super().validate()`` anymore and now + completely relies on ``AutocompleteInterface.validate_values()``. This was + how ``django-autocomplete-light`` was initially designed for, kudos to + @zhiyajun11 for pointing it out ! This optimises code which was doing + validation twice and gives the flexibility it was initially designed for + from within the Autocomplete class (#410). + + SQL BREAK + + Model Autocompletes now generate custom SQL be able to save the order in + which users have filled an autocomplete field. This actually comes from the + last 2.x version. + + CHANGES + + Most users won't notice the break except maybe the CSS ones and of course + also for Django 1.9 users. + + - #419: ANSI SQL compliance (@sbaum) + - #413: Exception when using models having primary key names different from + id. + - #412: Support models with a pk different than "id" and non-numeric. + (@mhuailin) + - #411: Android compatibility (js bind changes). + - #410: Removed double validation by not calling suport of ``Field.validate()``. + - #408: Support Django 1.8 change-link. + - #409: Compatibility with non-autocomplete inputs present in the widget + (@SebCorbin) + + CONTRIBUTING CHANGES + + Pip wheel has been temporarely disabled because django-autoslug broke it, + any help here is welcome, I did my best in the various fix/*wheel* + branches but Travis won xD + There's a mission to extract the JS part and package it as a standalone + jQuery library to get more pull requests on the JS / CSS part. It sounds + like a pretty good start in the JS / UI testing and packaging world. Any + help there is welcome. + CI now has tests against MySQL and PostgreSQL since we're generating custom + SQL. + + Again welcome to new contributors @mhuailin and @SebCorbin and thanks all + for reporting issues on GitHub with all needed details and forks which make + it easy to reproduce. + + And thanks to @blueyed who helped sinking this year's backlog like crazy. + +2.1.1 Not sure how the 2.1.0 release got on PyPi, marked "hidden", bumping and + uploading a new version rather than removing it as it might have already + been deployed somewhere. + +2.1.0 Final release. + +2.1.0rc4 + + - #331: Fixed reverse order of items in deck, by @smcoll. + +2.1.0rc3 + + - PEP396: autocomplete_light.__version__. + - #401: Easy to subclass FieldBase for custom ModelChoiceField (@smcoll), + - #397: Support defining Meta in a ModelForm's parent. + - #394: Add comment about keyCode issue with Webkit; check for charCode==0 + +2.1.0rc2: Maintain BC again in autocomplete_light ns until dj19 + +2.1.0rc1: Friendship release + + BACKWARD COMPATIBILITY BREAK + + There was a bug which caused registering an autocomplete with a model + attribute to be renamed by the registry to prefix the autocomplete name + with the model name. This should not be the case anymore, but some + autocomplete names might have changed in your project if you've been + working around that bug by using the buggy name rather than forcing name= + uppon registration. Example: + + class Foo(YourBaseAutocomplete): + model = Bar + autocomplete_light.register(Foo) + + The above example Would cause 'FooBar' to be registered, but now it's just + 'Foo' as expected. So if you're using 'FooBar' instead of 'Foo' anywhere it + should break. See #323 / 255263e61 for gory details. + + DEPRECATION WARNING + + Because Django 1.9 is so sharp on import, ``import autocomplete_light`` + should become ``import autocomplete_light.shortcuts as autocomplete_light`` + in your projects. Backward compatibility will be maintained, but you don't + get all the shortcuts from ``import autocomplete_light`` anymore starting + Django 1.9. + + - Django 1.8, 1.9 support: fixed warnings, use pytest-django + - #383: Support ModelForms without Meta.model + - #313: Model not acquired from AutocompleteModelBase + - #323: Only derivate the autocomplete name if both model and autocomplete + are passed + - #378: Be friendly with special html chars in values (David Aurelio) + - #381: The "open parentheses" (shift+9) cannot be typed on autocomplete + field. + - #380: PyPy support: PyPy2 tests passes without genericm2m and without + taggit. PyPy3 tests won't run because lxml won't install. (Piet Delport) + - #374: Update clear-fix with latest version, + - Also Daniel Hahler made an outstanding job in the test area, builds are a + lot faster, using tox, pytest-django. + - Daniel Hahler is the second owner of YourLabs org, way to go ! + + So we got 7 bugs fixed, PyPy support, Django 1.8 and Django 1.9 support (we + support 1.4 to 1.9 now). + + Thanks a lot to Daniel Hahler for having couch-hosted me the last 7 days in + Berlin, we've been hacking like crazy, it was really awesome, I went there + for some hacking and now I know I have a friend for life. + + See you for the next django-autocomplete-light sprint ;) Who's hosting BTW + ? + +2.0.9: + + - #257: Got rid of RemovedInDjango18Warning deprecation warnings. + - #371: Skip unuseable virtualfields, like VoteManager for django-vote. + - #372: New PEP8 E731 support. + +2.0.7: + + - Removed unused module (autocomplete_light.generic), + - choices_for_request: assert that search_fields is not a string + - #354: Choices=[] should not crash. + - also: cleaned 2.x, code coverage increase + - also: stable/2.x.x deprecated in favor of master + - also: tests against django (1.8) master enabled but allowed to fail + - also: promoted @blueyed to core-dev, rock'on B) + +2.0.6: #282: add-another in admin too (see message for 02a555cef3d) + +2.0.5: Code removal + +2.0.4: #347: Django 1.7 AppConfig support + +2.0.3: #337: Fixed error in text_widget.js + +2.0.2 + + - #334: Fixed display of autocomplete with clearfix (by @madEng84) + - #336: Added compiled po files + - #335: Removed debug statement + +2.0.1: Initial stable release + +2.0.0a18: #292: Enable clear-ing nullable GFKs with the form. + +2.0.0a17: #280: Do not depend on django on install. + +2.0.0a16: + + - #252: Not all INSTALLED_APPS are modules (Django 1.7), + - #270: SelectMultipleHelpTextRemovalMixin multiple language support, + - #267: Delegate add-another url generation to Autocomplete class, + - #269: make build_widget_attrs thread-safe, + - #250: Fix ChoiceField.get_choices(), + - #192: Auto-load local jquery if not loaded already, + - Django 1.7b2 support. + +2.0.0a15: + + - Honor Meta.widgets, + - Added .modern-style class for autocomplete widget, + +2.0.0a14: Added missing extensions to MANIFEST + +2.0.0a13: Fix #244: Added ajax loading in input.autocomplete. + +2.0.0a12: !! BC BREAK !! Renamed input_attrs to attrs to be consistent + with django. + +2.0.0a11: + + - #241 IE11 compatibility fix on text autocomplete contributed by Jonathan + Wiklund (@MaZZly) + + +2.0.0a10: + + - Implemented autocomplete_names support to override the default + Autocomplete class used for a given model form field, + - Make the first Autocomplete class the default autocomplete (for generic + and model). + - Implemented lazy autocompletes: you can refer to an autocomplete class + before it is discovered. + - Import TaggitField and TaggitWidget in autocomplete_light. + +2.0.0a9: + + - Fix #219: autocompletes now convert None value to an empty list, as + Autocomplete promises that values is a list. (Fixed gfk in inlines) + - Fix #226: improved css/js positioning. + - Fix #231: widget.js was re-initializing widgets. + - Fixed taggit support + - Fix: label was not set for gmtm and gfk in inlines. + +2.0.0a8: + + - Updated pt_BR translation contributed by Fábio C. Barrionuevo da Luz (@luzfcb). + +2.0.0a7: + + - Removed a layer of complexity by trading autocomplete_js_attributes and + widget_js_attributes for attrs (for input/autocomplete object) and widget_attrs + (for widget container/widget js object). + - Fixed naming conventions issues: + - #124: css class .choiceDetail should be .choice-detail, same for + .choiceUpdate + - #97: rename .div css class to .block, + - Fixed #84: data-autocomplete-placeholder is gone in favor of the + placeholder attribute. + - Fixed #82: data-* is not parsed anymore by yourlabs.Widget, data-widget-* + is parsed for options instead. + - Fixed #81: js Autocomplete object now parses data-autocomplete-* for + options, + - Fixed #80: js maxValues is now maximumValues. + - Fixed #27: refactored selenium tests to be able to test any example app. + - Bugfix: get_autocomplete_from_arg returned wrong autocomplete in two cases + +2.0.0a6: + + - Simplified widget template selection. + +2.0.0a5: + + - Support for Django 1.6's ModelForm.Meta.fields = '__all__', generic fk + and generic m2m autocomplete fields were not created. + +2.0.0a4: + + - Fix #204: Restore AutocompleteModelBase as default, + - Fix #205: Support Python3 in views, + - Fixed 404 response in AutocompleteView. + +2.0.0a3: + + - Do not interfere with non-autocomplete-enabled form fields, + - Ported taggit support to Python3, + - Renamed TaggitTagField to TaggitField and integrated TaggitField into + autocomplete_light.ModelForm, + +2.0.0a2: + + - Make AutocompleteModelTemplate the default autocomplete base for models. + - Added ChoiceField and MultipleChoiceField. + - Renamed .div class to .block, backward compatibility is preserved. + - BC Break: Restyled autocomplete widget to look more modern. If you want + the old style back, include autocomplete_light/old_style.css. + +2.0.0a1: + + - Fix #168: ensure that autocompletion inputs behaves like django + when it comes to values that were removed from the queryset. + +2.0.0pre: + + There are minor backward compatibility breaks due to the + reorganization of form classes. The best way to upgrade is to + inherit from autocomplete_light.ModelForm. + + - autocomplete_light.FixedModelForm was renamed to + autocomplete_light.SelectMultipleHelpTextRemovalMixin and is now + a mixin for ModelForm. Inherit from both that and + django.forms.ModelForm to get the same. + - GenericModelForm was renamed to + GenericM2MRelatedObjectDescriptorHandlingMixin and is now a mixin + for ModelForm. Inherit from both that and django.forms.ModelForm + to get the same. + - get_widgets_dict is gone, because we now favor using form fields + which do validation where it's due: on POST. + - using widgets should still work, but using the new form fields is + better because it enables DRY validation: you don't have to + hardcode the queryset on the form field definition anymore. + + To fix those, just inherit from autocomplete_light.ModelForm if + possible, else check the form API details in the source or + generated API documentation: + + - https://django-autocomplete-light.readthedocs.io/en/v2/form.html#module-autocomplete_light.forms + + New features: + + - Added form fields, which are now in charge of validation, + - Added ModelForm which includes all features previously provided as + separated tools (generic m2m support, django issue #9321, etc, etc ...) + and overrides django's form field generator to automatically use + autocomplete fields when possible, even for generic relations. + - Python3 support. + - Tests rely on apps provided in the autocomplete_light.tests.apps + submodule instead of those provided by test_project. This make tests + potentially runnable on a user project (except that the user project + still needs to add those apps in INSTALLED_APPS). + +1.4.9: Fix #193: IE8 support hacks. + +1.4.8: Fix #195: RegistryView got broken on recent Django versions. + +1.4.7: + + - Fix #190: Remove Django 1.6 Depreciation Warning, + - Fix #189: Fixed support for USE_THOUSAND_SEPARATOR. + +1.4.6: + + - Fix #188: Added debouncing in autocomplete.js, + - Fix #173: load static from staticfiles. + +1.4.5: + + - Fix #180: Support HTTPS in remote.js, + - Fix #184: Mouse support upgrade in focus management, + - Fix #182: Removed hard-coded class="" attribute. + +1.4.4: Improved IE7 and IE8 support, thanks @rhunwicks + +1.4.3: + + - Fix Added option clearInputOnSelectChoice, patch by @vosi + - Fix uninitialized js key, patch by @vosi + - Escape choices before putting them in html, patch by @voidus + - Added Simon Kohlmeyer to AUTHORS (Volodymyr was there already). + +1.4.2 Fix #164: Support iterables in AutocompleteModel.order_by. + +1.4.1 + + - Fix #163: Use ._default_manager instead of .objects. Thanks @kakulukia + +1.4.0 + + - BC Break: Default split_words to False again, to **not** match behaviour + from django.contrib.admin which is definitively too naive. (Thanks + kezabelle for helping in this decision). This means that the default + behaviour is like pre-1.3.0 again. + - Enable split_words='or' to use "OR" conditions on splitted words. + - Set order_by=None by default in AutocompleteModel. + - Added missing docstrings. + - Made it easier to spot failing tests. + - Covered all combinations with tests. + +1.3.1 + + - Support django 1.5 RelatedFieldWidgetWrapper in + FixedModelForm, + - Support django-responsive-admin theme (and probably many + other customisations) by adding a div with clear:both after + the widget. + +1.3.0 + + - BC Break: Default split_words to True, to match behaviour from + django.contrib.admin. + - Added split_words and improved search_fields support for + AutocompleteGeneric. + - Fix #161: Avoid focusing on next field on widget initialization. + +1.2.6 Fixed wrong docstring. + +1.2.5 Raise AutocompleteNotRegistered instead of KeyError in + Registry.__getitem__. + +1.2.4 @g1itch: Support hyphens in generic autocomplete object pks. + +1.2.3 @mislavcimpersak: Croatian i18n + +1.2.2 @g1itch: Rusisan i18n + +1.2.1 @jojax: Fix innefective test on is popup, in autocomplete CreateView + +1.2.0 + +- Fix #132: removed dead line of code, and restored compatibility with + autocomplete-light <1.1.29, +- Implement #136: AutocompleteModel.split_words +- Auto-hilight the first choice. +- Fix #135: AutocompleteModel is not compatible with Django 1.3 + +1.1.31 Fix #129: Firefox support in JS positioning code. + +1.1.30 Polish translation contributed by Michał Pasternak. + +1.1.29 Fix #126: support .clone(true) with values. + +WARNING: This version broke cloning widgets in some cases. This was reported in +#132 and fixed in 1.2.0. + +1.1.28 Regression fix: hide choiceDetail and choiceUpdate from the autocomplete + box. + +This fixes the default model template, see in test_project: + + http://localhost:8000/admin/default_template_autocomplete/testmodel/add/ + +The test_project now includes demos created by Michał Pasternak. Thankssss ! + +1.1.27 Fix #117: django-grappelli support. + +Contributed by Michał Pasternak. + +1.1.26 + +- Fixed #114: Use widget_template attr only when set. +- Fixed #111: Support jQuery's clone(true). + +1.1.25 Fixed #108: generic inline support. + +1.1.24 + +- Allow to override the default AutocompleteModelBase class used by register() +- Implemented django-hvad support + +1.1.23: Removed django from install_requires, fixes pip install -U + +1.1.22: Fixed DeprecationWarning for Django 1.5. + +1.1.21 Bugfix: box was not re-shown after inputing twice the same string +of minimumCharacters length. + +1.1.20 Click on modal overlay closes the popup windo + +1.1.19 Make add-another popup a modality + +1.1.18 Center add-another popup on screen + +1.1.17 Update fr locale + +1.1.16 Django 1.5 compatibility. Credit: Patrick Taylor + +1.1.15 Fix #99: choices kwarg was ignored + +1.1.14 Bugfix: fetchComplete now ready for textStatus == 'abort' + +1.1.13 + +- Enhancement #78: search_fields more like admin, +- Bugfix: closed brackets in style.css (thanks papalagichen) + +1.1.12 Enabled CSRF. + +1.1.11 Fixed dynamic initialisation (ie. inlines). + +Bug was caused by a wrong attempt at migrating from jq17 live() to jq19 on(). + +1.1.10 + +- Partial autocomplete.js rewrite using ideas from bootstrap-typeahead.js, +- Use of $.proxy() which allowed to get rid of many scope hacks like: var + autocomplete = this; +- When the maximum number of choices has been selected and the input goes, + automatically focus on next visible field, +- Do not show autocomplete if input has below minCharacters, +- CSS rewrite/debloat (~-50% SLOCs), +- Backported typeahead CSS, +- Backported typeahead autocomplete box placement strategy, hoping to fix IE10 + mobile support, +- jQuery 1.9 support (thanks Nicolas Seinlet for the heads up). + +1.1.9 + +- Use explicit check for None value instead of trueness, allowing pk=0 (thanks + @ekohl). +- Widget requires proper url setup (thanks @emesik). +- Rewrote keypress handler using recent contributions. + +Also credits to @ekohl and @emesik for working on keypress handling (added to +AUTHORS). + +1.1.8 (was not released on PyPi) + +- Alias autocomplete_light.FixedModelForm to autocomplete_light.ModelForm. + +1.1.7 Reset default hideAfter to 500, or click support is half broken. + +1.1.6 + +- Fix #77: autocomplete.js "was not as good as bootstrap typeahea", +- Fix #72: use prototypes in JS to make code faster + +Credit to Gennady (@geaden) for having the guts to criticize autocomplete.js. + +1.1.5 + +- Added blocks in autocomplete_light/widget.html which makes it easier to + extend - without boilerplate. +- Refactor CreateView to make it easier to extend. +- Added widget.extra_context to make it easier to add context to the widget + template. + +1.1.4 Enter submits the form is there is no selected choice. + +Added to AUTHORS: Ewoud Kohl van Wijngaarden + +1.1.3 Fix #71: Refactor HTML generation parts in widget.js + +1.1.2 + +- Fix #68: bug when using TextInput with a custom class, +- Fix #30: exclude already selected items from model autocomplete, +- Fix #60: got rid of updateWidgets(), +- Fix #63: default template for AutocompleteModelTemplate, + +1.1.1 + +- Added option to disable watch of autocompletes hidden selects, +- Performance improvement on inlines, +- Provide: autocomplete_light.FixedModelForm, +- Allow override of form kwarg in modelform_factory, +- taggit support: warn that commit=False is not supported by TagField, +- Optimize updateWidgets with the help of DOMNodeInserted, +- Avoid ImproperlyConfigured when using CreateView directly for popups only, +- Mentioned python 2.6 support, +- Django 1.5 support in test project + +1.1.0 BC break in templates. + +- Fix #51, replaced
tags by , this + enables rendering the widget in inline tags such as with {{ + form.as_p }}, and also makes it valid HTML. +- Fix #58, remove restrictions in choices_for_values. +- Added to AUTHORS: Marc Hörsken. + +1.0.26 Fix #25: Prevent accidental inclusion of autocomplete_light/static.html + +1.0.25 Added support for django-taggit in django.contrib.taggit_fields. + +1.0.24 Added pt_BR translation, thanks Fábio C. Barrionuevo da Luz. + +1.0.23 Added FixedModelForm, workaround django issue #9321. + +1.0.22 Fix #46: $.data striped leading zeros from data-value + +1.0.21 Added addanother.js, allowing to 'add another' even outside the admin. + +1.0.20 Added AutocompleteBase.empty_html_format and spanish translation. + +1.0.19 Locale/UX improvements suggested by vibragiel. + +1.0.18 CSS Improvements by Michal Pasternak + +1.0.17 Fixed a bug with cache concerning keyboard navigation and dependent + autocompletes. + +1.0.16 Added CharField autocompletion for comma separated values. + +1.0.15 Holliday release + + - Fix #31: Workaround firefox form tempering attitude on reload + - Fix #33: Rename LICENSE.txt to LICENSE + - Fix #34: STATIC_PREFIX changed to {% static %} in templates + - Fix #34: Added readme in russian + - Fix #36: Removed old javascript-placeholder code from widget.js + - Fix #38: Invalid CSS background property + - Added to AUTHORS: @balmaster, @vosi, Brant Young (I hope i didn't + forget anyone, if so please let me know) + + Note that except for #31, fixes were provided by the community, thanks a + lot ! + +1.0.14 Added support for custom javascript initialization on autocomplete + creation, ie. inlines. + + It is backward compatible, but you should migrate your custom javascript + to: + + 0) Use $(document).bind('yourlabsWidgetReady', ...) instead of + $(document).ready(...) to initialize autocomplete widgets + 1) Use + $('.autocomplete-light-widget[data-bootstrap=...]').live('initialize', ...) + to instanciate autocomplete widgets instead of + $('.autocomplete-light-widget[data-bootstrap=...]').each(...) + + See docs or remote.js for an example. + +1.0.13 Use HTML5 placeholder, javascript API support for cloned widgets. + +1.0.12 Fixed a crash when yourlabsAutocomplete was called on no element, fixed + div id and removed debug statement. + +1.0.11 Fugbix AutocompleteRestModel could not rely only on uniques, so it + relies on get_or_create_by in that case + +1.0.10 Fugbix models with a non numerical pk where not accepted anymore by a + spec, the spec was removed from tests and this case is fixed. + +1.0.9 Bugfix in remote autocomplete + +1.0.8 Fixed keyboard navigation, 1.0.7 broke tests + +1.0.7 Tested support for autocompletes that depend on each other. + +1.0.6 Enhancement: set class 'autocomplete-light-widget' to autocomplete outer + containers created by widget.js + +1.0.5 Enhancement: made rest model local object fetcher less harsh when trying + to find a local object + +1.0.4 Bugfix: registering a generic autocomplete with a custom name did not + use the custom name + +1.0.3 Default search_names to 'name' if the model has a name field + +1.0.2 Bugfix & refactor django admin + popup + +1.0.1 Slight ergonomy improvement + +1.0.0 (Almost) full rewrite + + - "Channel" is gone in favor of "Autocomplete" + - More unit tests and new selenium tests + - Javascript rewrite, for consistency and documentation + - New view on /autocomplete_light/ allows admins to view the registry + +0.6 No pity for insane bloat removal + + Backward compatibility break: {% autocomplete_light_static %} was removed. + Instead, use {% include 'autocomplete_light/static.html' %} as demonstrated + in the "Quick start" documentation. + + Note that autocomplete_light/static.html does not include + cities_light/autocomplete.js, so you have to load it yourself now. + +0.5 + + No backward compatibility break. + + - Bugfix in generic channel example: demonstrate how to query_filter on + multiple queryset types. + - Bugfix in GenericForeignKeyField: allow none value. + - Enhancement: autocomplete_light.contrib.generic_m2m supports generic many + to many relations. + - Enhancement: allow registration of model with custom channel attributes, + this allows to register a model with another "search_name" than "name" + without subclassing. + - Documentation improvements. diff --git a/CHANGES.txt b/CHANGES.txt new file mode 120000 index 000000000..a75130bcc --- /dev/null +++ b/CHANGES.txt @@ -0,0 +1 @@ +CHANGELOG \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..de7bb1178 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2008-2011 James Pic and contributors. + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 000000000..b67a829cf --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,4 @@ +include *.rst *.txt README LICENSE AUTHORS CHANGELOG +recursive-include src *.html *.css *.js *.py *.po *.mo *.json *.png *.gif *.otf *.eot *.svg *.ttf *.woff +prune src/dal_select2/static/autocomplete_light/vendor/select2/docs +prune src/dal_select2/static/autocomplete_light/vendor/select2/node_modules diff --git a/README b/README new file mode 100644 index 000000000..df0a6e568 --- /dev/null +++ b/README @@ -0,0 +1,50 @@ +.. image:: https://img.shields.io/pypi/dm/django-autocomplete-light.svg + :target: https://pypi.python.org/pypi/django-autocomplete-light +.. image:: https://badge.fury.io/py/django-autocomplete-light.png + :target: http://badge.fury.io/py/django-autocomplete-light +.. image:: https://secure.travis-ci.org/yourlabs/django-autocomplete-light.png?branch=master + :target: http://travis-ci.org/yourlabs/django-autocomplete-light +.. image:: https://codecov.io/github/yourlabs/django-autocomplete-light/coverage.svg?branch=master + :target: https://codecov.io/github/yourlabs/django-autocomplete-light?branch=master + +Features +-------- + +- Python 2.7, 3.4, Django 2.0+ support (Django 1.11 (LTS), is supported until django-autocomplete-light-3.2.10), +- Django (multiple) choice support, +- Django (multiple) model choice support, +- Django generic foreign key support (through django-querysetsequence), +- Django generic many to many relation support (through django-generic-m2m and + django-gm2m) +- Multiple widget support: select2.js, easy to add more. +- Creating choices that don't exist in the autocomplete, +- Offering choices that depend on other fields in the form, in an elegant and + innovative way, +- Dynamic widget creation (ie. inlines), supports YOUR custom scripts too, +- Provides a test API for your awesome autocompletes, to support YOUR custom + use cases too, +- A documented automatically tested example for each use case in test_project. + +Roadmap +------- + +- DAL < 4 supports Python 2 and 3 and Django 1.8 to 3.x +- DAL 4 supports Django 3.x and 4.x and Python 3 +- DAL >= 3.9 offers a modern alternative to select2 + +Resources +--------- + +- `**Documentation** graciously hosted + `_ by `RTFD + `_ +- `Mailing list graciously hosted + `_ by `Google + `_ +- For **Security** issues, please contact yourlabs-security@googlegroups.com +- `Git graciously hosted + `_ by `GitHub + `_, +- `Package graciously hosted + `_ by `PyPi + `_, diff --git a/README.rst b/README.rst new file mode 120000 index 000000000..100b93820 --- /dev/null +++ b/README.rst @@ -0,0 +1 @@ +README \ No newline at end of file diff --git a/changelog.py b/changelog.py new file mode 100644 index 000000000..a16d6343d --- /dev/null +++ b/changelog.py @@ -0,0 +1,91 @@ +from datetime import datetime +from github import Github +from pprint import pprint +import os +import re +import requests +import subprocess +import sys + +g = Github(os.getenv('GITHUB_TOKEN')) +repo = g.get_repo('yourlabs/django-autocomplete-light') +tag = sys.argv[1] + + +with open('CHANGELOG', 'r') as f: + CHANGELOG = f.read() + +last_release = None +for line in CHANGELOG.split('\n'): + if match := re.match('^(.+)', line): + last_release = match.group(0) + break + +git_log = subprocess.check_output( + f"git log --pretty=format:'%h %D' {last_release}..", + shell=True, +).decode('utf8').split('\n') + +tags = {} +tag_commits = [] +for line in git_log: + parts = line.split() + if len(parts) == 1: + tag_commits.append(parts[0]) + continue + + for i, part in enumerate(parts): + if part == 'tag:': + tags[tag] = tag_commits + tag = parts[i + 1].strip(',') + tag_commits = [parts[0]] + +# add remaining commits to last seen tag +tags[tag] = tag_commits + +lines = [] +for tag, shas in tags.items(): + lines.append(tag) + lines.append('') + + for sha in shas: + line = [] + author, description = subprocess.check_output( + f"git log --format='%an--sep--%B' -n1 {sha}", + shell=True, + ).decode('utf8').split('\n')[0].split('--sep--') + if description.startswith('Release '): + date = subprocess.check_output( + f"git log --format='%as' -n1 {sha}", + shell=True, + ).decode('utf8').strip() + lines.append(f' {date} {description}') + continue + try: + commit = repo.get_commit(sha) + pulls = [*commit.get_pulls()] + except: # commit not found + lines.append(f' {sha} {description} by {author}') + continue + numbers = [] + users = [] + for pull in pulls: + numbers.append(pull.number) + users.append(pull.user.login) + for number in numbers: + line.append(f'#{number}'.ljust(7)) + if not numbers: + line.append(sha) + + line.append(description) + line.append('by') + for user in users: + line.append(f'@{user}') + if not users: + line.append(author) + lines.append(' ' + ' '.join(line)) + + lines.append('') + + +print('\n'.join(lines)) diff --git a/conftest.py b/conftest.py new file mode 100644 index 000000000..8485d549a --- /dev/null +++ b/conftest.py @@ -0,0 +1,8 @@ +import pytest +import os + + +@pytest.fixture(scope='session') +def splinter_webdriver(): + """Override splinter webdriver name with BROWSER env variable.""" + return os.environ.get('BROWSER', 'firefox') diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 000000000..280ad0c24 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,216 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# User-friendly check for sphinx-build +ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) +$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) +endif + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help +help: + @echo "Please use \`make ' where 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 " applehelp to make an Apple Help Book" + @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 " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " xml to make Docutils-native XML files" + @echo " pseudoxml to make pseudoxml-XML files for display purposes" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + @echo " coverage to run coverage check of the documentation (if enabled)" + +.PHONY: clean +clean: + rm -rf $(BUILDDIR)/* + +.PHONY: html +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +.PHONY: dirhtml +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +.PHONY: singlehtml +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +.PHONY: pickle +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +.PHONY: json +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +.PHONY: htmlhelp +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." + +.PHONY: qthelp +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-autocomplete-light.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/django-autocomplete-light.qhc" + +.PHONY: applehelp +applehelp: + $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp + @echo + @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." + @echo "N.B. You won't be able to view it unless you put it in" \ + "~/Library/Documentation/Help or install it in your application" \ + "bundle." + +.PHONY: devhelp +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-autocomplete-light" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/django-autocomplete-light" + @echo "# devhelp" + +.PHONY: epub +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +.PHONY: latex +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)." + +.PHONY: latexpdf +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." + +.PHONY: latexpdfja +latexpdfja: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through platex and dvipdfmx..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +.PHONY: text +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +.PHONY: man +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +.PHONY: texinfo +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +.PHONY: info +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +.PHONY: gettext +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +.PHONY: changes +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +.PHONY: linkcheck +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." + +.PHONY: doctest +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." + +.PHONY: coverage +coverage: + $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage + @echo "Testing of coverage in the sources finished, look at the " \ + "results in $(BUILDDIR)/coverage/python.txt." + +.PHONY: xml +xml: + $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml + @echo + @echo "Build finished. The XML files are in $(BUILDDIR)/xml." + +.PHONY: pseudoxml +pseudoxml: + $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml + @echo + @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." diff --git a/docs/_ext/djangodocs.py b/docs/_ext/djangodocs.py new file mode 100644 index 000000000..84cd155db --- /dev/null +++ b/docs/_ext/djangodocs.py @@ -0,0 +1,21 @@ +def setup(app): + app.add_crossref_type( + directivename = "setting", + rolename = "setting", + indextemplate = "pair: %s; setting", + ) + app.add_crossref_type( + directivename = "ref", + rolename = "ref", + indextemplate = "pair: %s; ref", + ) + app.add_crossref_type( + directivename = "doc", + rolename = "doc", + indextemplate = "pair: %s; doc", + ) + app.add_crossref_type( + directivename = "label", + rolename = "label", + indextemplate = "pair: %s; label", + ) diff --git a/docs/api.rst b/docs/api.rst new file mode 100644 index 000000000..72110b7b5 --- /dev/null +++ b/docs/api.rst @@ -0,0 +1,154 @@ +Python API +========== + +Views +----- + +.. automodule:: dal.views + :members: + +Widgets +------- + +.. automodule:: dal.widgets + :members: + +FutureModelForm +=============== + +.. automodule:: dal.forms + :members: + +dal_select2 +=========== + +This is a front-end module: it provides views and widgets. + +Views +----- + +.. automodule:: dal_select2.views + :members: + +Widgets +------- + +.. automodule:: dal_select2.widgets + :members: + +Fields +------ + +.. automodule:: dal_select2.fields + :members: + +Test tools +---------- + +.. automodule:: dal_select2.test + :members: + +dal_contenttypes +================ + +Fields +------ + +.. automodule:: dal_contenttypes.fields + :members: + +dal_select2_queryset_sequence +============================= + +Views +----- + +.. automodule:: dal_select2_queryset_sequence.views + :members: + +Fields +------ + +.. automodule:: dal_select2_queryset_sequence.fields + :members: + +Wigets +------ + +.. automodule:: dal_select2_queryset_sequence.widgets + :members: + +dal_queryset_sequence +===================== + +Views +----- + +.. automodule:: dal_queryset_sequence.views + :members: + +Fields +------ + +.. automodule:: dal_queryset_sequence.fields + :members: + +Widgets +------- + +.. automodule:: dal_queryset_sequence.widgets + :members: + +dal_gm2m_queryset_sequence +========================== + +Fields +------ + +.. automodule:: dal_gm2m_queryset_sequence.fields + :members: + +dal_genericm2m_queryset_sequence +================================ + +Fields +------ + +.. automodule:: dal_genericm2m_queryset_sequence.fields + :members: + +dal_gm2m +======== + +Fields +------ + +.. automodule:: dal_gm2m.fields + :members: + +dal_genericm2m +============== + +Fields +------ + +.. automodule:: dal_genericm2m.fields + :members: + +dal_select2_taggit +================== + +Fields +------ + +.. automodule:: dal_select2_taggit.widgets + :members: + +dal_select2_tagging +=================== + +Fields +------ + +.. automodule:: dal_select2_tagging.widgets + :members: diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 000000000..0e2ac00e8 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,379 @@ +# -*- coding: utf-8 -*- +# +# django-autocomplete-light documentation build configuration file, created by +# sphinx-quickstart on Wed Feb 3 02:03:57 2016. +# +# 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 sys +import os + + +# 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.append( + os.path.abspath(os.path.join(os.path.dirname(__file__), "_ext"))) +sys.path.insert(0, os.path.abspath('../src/')) +sys.path.append( + os.path.abspath(os.path.join(os.path.dirname(__file__), "../test_project"))) + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'settings') +import django +django.setup() + +# -- 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 = [ + 'djangodocs', + 'sphinx.ext.autodoc', + 'sphinx.ext.intersphinx', + 'sphinx.ext.viewcode', +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# source_suffix = ['.rst', '.md'] +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-autocomplete-light' +copyright = u'2012-2021, YourLabs' # NGO +author = u'James Pic & Amazing Contributors' + +# 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 = u'3.9' +# The full version, including alpha/beta/rc tags. +release = u'3.9.0rc1' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +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 = [] + +# If true, keep warnings as "system message" paragraphs in the built documents. +#keep_warnings = False + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = False + + +# -- 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 = 'furo' + + +# 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 +# " v 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 = [] + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +#html_extra_path = [] + +# 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 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 + +# Language to be used for generating the HTML full-text search index. +# Sphinx supports the following languages: +# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' +# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' +#html_search_language = 'en' + +# A dictionary with options for the search language support, empty by default. +# Now only 'ja' uses this config value +#html_search_options = {'type': 'default'} + +# The name of a javascript file (relative to the configuration directory) that +# implements a search results scorer. If empty, the default will be used. +#html_search_scorer = 'scorer.js' + +# Output file base name for HTML help builder. +htmlhelp_basename = 'django-autocomplete-lightdoc' + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', + +# Latex figure (float) alignment +#'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, 'django-autocomplete-light.tex', u'django-autocomplete-light Documentation', + u'James Pic \\& contributors', '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 + +# 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 = [ + (master_doc, 'django-autocomplete-light', u'django-autocomplete-light Documentation', + [author], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, 'django-autocomplete-light', u'django-autocomplete-light Documentation', + author, 'django-autocomplete-light', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +#texinfo_no_detailmenu = False + + +# -- Options for Epub output ---------------------------------------------- + +# Bibliographic Dublin Core info. +epub_title = project +epub_author = author +epub_publisher = author +epub_copyright = copyright + +# The basename for the epub file. It defaults to the project name. +#epub_basename = project + +# The HTML theme for the epub output. Since the default themes are not +# optimized for small screen space, using the same theme for HTML and epub +# output is usually not wise. This defaults to 'epub', a theme designed to save +# visual space. +#epub_theme = 'epub' + +# The language of the text. It defaults to the language option +# or 'en' if the language is not set. +#epub_language = '' + +# The scheme of the identifier. Typical schemes are ISBN or URL. +#epub_scheme = '' + +# The unique identifier of the text. This can be a ISBN number +# or the project homepage. +#epub_identifier = '' + +# A unique identification for the text. +#epub_uid = '' + +# A tuple containing the cover image and cover page html template filenames. +#epub_cover = () + +# A sequence of (type, uri, title) tuples for the guide element of content.opf. +#epub_guide = () + +# HTML files that should be inserted before the pages created by sphinx. +# The format is a list of tuples containing the path and title. +#epub_pre_files = [] + +# HTML files that should be inserted after the pages created by sphinx. +# The format is a list of tuples containing the path and title. +#epub_post_files = [] + +# A list of files that should not be packed into the epub file. +epub_exclude_files = ['search.html'] + +# The depth of the table of contents in toc.ncx. +#epub_tocdepth = 3 + +# Allow duplicate toc entries. +#epub_tocdup = True + +# Choose between 'default' and 'includehidden'. +#epub_tocscope = 'default' + +# Fix unsupported image types using the Pillow. +#epub_fix_images = False + +# Scale large images. +#epub_max_image_width = 0 + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#epub_show_urls = 'inline' + +# If false, no index is generated. +#epub_use_index = True + + +# Example configuration for intersphinx: refer to the Python standard library. +intersphinx_mapping = { + 'python': ('http://docs.python.org/2.7', None), + 'django': ('http://docs.djangoproject.com/en/dev/', + 'http://docs.djangoproject.com/en/dev/_objects/'), + 'pypa': ('https://pypa.io/en/latest/', None), + 'pip': ('https://pip.pypa.io/en/latest/', None), + 'pypug': ('https://packaging.python.org/en/latest/', None), +} diff --git a/docs/genericm2m.rst b/docs/genericm2m.rst new file mode 100644 index 000000000..0b2d4012e --- /dev/null +++ b/docs/genericm2m.rst @@ -0,0 +1,67 @@ +django-generic-m2m +~~~~~~~~~~~~~~~~~~ + +Model example +============= + +Consider such a model, using `django-generic-m2m +`_ to handle generic +many-to-many relations: + +.. code-block:: python + + from django.db import models + + from genericm2m.models import RelatedObjectsDescriptor + + + class TestModel(models.Model): + name = models.CharField(max_length=200) + + locations = RelatedObjectsDescriptor() + + def __str__(self): + return self.name + +View example +============ + +The :ref:`generic-autocomplete-view` works here too: we're relying on Select2 +and QuerySetSequence again. + +Form example +============ + +As usual, we need a backend-aware widget that will make only selected choices +to render initially, to avoid butchering the database. As we're using a +QuerySetSequence and Select2 for multiple selections, we'll try +:py:class:`~dal_select2_queryset_sequence.widgets.QuerySetSequenceSelect2Multiple` +widget. + +Also, we need a field that's able to use a QuerySetSequence for choices to +validate multiple models, and then update the RelatedObjectsDescriptor +relations: +:py:class:`~dal_genericm2m_queryset_sequence.fields.GenericM2MQuerySetSequenceField`. + +Finnaly, we can't use Django's ModelForm because it doesn't support +non-editable fields, which RelatedObjectsDescriptor is. Instead, we'll use +:py:class:`~dal.forms.FutureModelForm`. + +Example: + +.. code-block:: python + + class TestForm(autocomplete.FutureModelForm): + locations = autocomplete.GenericM2MQuerySetSequenceField( + queryset=autocomplete.QuerySetSequence( + Country.objects.all(), + City.objects.all(), + ), + required=False, + widget=autocomplete.QuerySetSequenceSelect2Multiple( + 'location-autocomplete'), + ) + + class Meta: + model = TestModel + fields = ('name',) diff --git a/docs/gfk.rst b/docs/gfk.rst new file mode 100644 index 000000000..5c0e3cff5 --- /dev/null +++ b/docs/gfk.rst @@ -0,0 +1,112 @@ +GenericForeignKey +~~~~~~~~~~~~~~~~~ + +Model example +============= + +Consider such a model: + +.. code-block:: python + + from django.contrib.contenttypes.fields import GenericForeignKey + from django.db import models + + + class TestModel(models.Model): + name = models.CharField(max_length=200) + language = models.CharField(max_length=200) + + content_type = models.ForeignKey( + 'contenttypes.ContentType', + null=True, + blank=True, + editable=False, + ) + + object_id = models.PositiveIntegerField( + null=True, + blank=True, + editable=False, + ) + + location = GenericForeignKey('content_type', 'object_id') + + def __str__(self): + return self.name + +.. _generic-autocomplete-view: + +Form example +============ + +To enable the use of automatic views we need to add 'dal_queryset_sequence' + to :django:setting:`INSTALLED_APPS`. + +First, we can't use Django's ModelForm because it doesn't support +non-editable fields, which GenericForeignKey is. Instead, we'll use +:py:class:`dal.forms.FutureModelForm`. + +Then we need to add the +:py:class:`dal_select2_queryset_sequence.fields.Select2GenericForeignKeyModelField` +field, with model_choice as keyword: this is a list of tuple, with the models +you want in the autocompletion and the validation, and the value of the +attribute of the model you want to query in the widget searchbox. Optionally, +you can forward an existing field in the form to filter an attribute of the +model, by adding a list of tuple containing the field to forward and the value +to filter. In this example, the text inserted in the language field will +filter the country models by their 'spoken_language' +attribute. + +Result: + +.. code-block:: python + + from dal import autocomplete # don't forget to pip install django-querysetsequence + + class TestForm(autocomplete.FutureModelForm): + + location = autocomplete.Select2GenericForeignKeyModelField( + # Model with values to filter, linked with the name field + model_choice=[(Country, 'country_code', [('language', 'spoken_language'),]), + (City, 'name')], + ) + + class Meta: + model = TestModel + +If you want to use your own widgets and views, assuming the widget takes an url as argument +and the view takes a queryset in its "as_view()" method, you can use +:py:class:`~dal_queryset_sequence.fields.GenericForeignKeyModelField`: + +.. code-block:: python + + from dal import autocomplete + + class TestForm(autocomplete.FutureModelForm): + + location = autocomplete.GenericForeignKeyModelField( + model_choice=[(Country,), (City,)], # Models + widget=autocomplete.QuerySetSequenceSelect2, + view=autocomplete.Select2QuerySetSequenceView, + ) + + class Meta: + model = TestModel + +In this example, we took :py:class:`~dal_select2_queryset_sequence.widgets.QuerySetSequenceSelect2` as the +custom widget and :py:class:`~dal_select2_queryset_sequence.views.Select2QuerySetSequenceView`. + + +Register the view for the form +============================== + +In url.py: + +.. code-block:: python + + from .forms import TestForm + + urlpatterns = [...] # your regular url patterns + urlpatterns.extend(TestForm.as_urls()) + +It will enable the search box to query and filter the results diff --git a/docs/gm2m.rst b/docs/gm2m.rst new file mode 100644 index 000000000..2eaef17ae --- /dev/null +++ b/docs/gm2m.rst @@ -0,0 +1,66 @@ +django-gm2m GM2MField +~~~~~~~~~~~~~~~~~~~~~ + +Model example +============= + +Consider such a model, using `django-gm2m +`_ to handle generic +many-to-many relations: + +.. code-block:: python + + from django.db import models + + from gm2m import GM2MField + + + class TestModel(models.Model): + name = models.CharField(max_length=200) + + locations = GM2MField() + + def __str__(self): + return self.name + +View example +============ + +The :ref:`generic-autocomplete-view` works here too: we're relying on Select2 +and QuerySetSequence again. + +Form example +============ + +As usual, we need a backend-aware widget that will make only selected choices +to render initially, to avoid butchering the database. As we're using a +QuerySetSequence and Select2, we'll try +:py:class:`~dal_select2_queryset_sequence.widgets.QuerySetSequenceSelect2Multiple` +widget. + +Also, we need a field that's able to use a QuerySetSequence for choices to +validate multiple models, and then update the GM2MField relations: +:py:class:`~dal_gm2m_queryset_sequence.fields.GM2MQuerySetSequenceField`. + +Finnaly, we can't use Django's ModelForm because it doesn't support +non-editable fields, which GM2MField is. Instead, we'll use +:py:class:`~dal.forms.FutureModelForm`. + +Example: + +.. code-block:: python + + class TestForm(autocomplete.FutureModelForm): + locations = autocomplete.GM2MQuerySetSequenceField( + queryset=autocomplete.QuerySetSequence( + Country.objects.all(), + City.objects.all(), + ), + required=False, + widget=autocomplete.QuerySetSequenceSelect2Multiple( + 'location-autocomplete'), + ) + + class Meta: + model = TestModel + fields = ('name',) diff --git a/docs/img/all.png b/docs/img/all.png new file mode 100644 index 0000000000000000000000000000000000000000..5451fb5ef2367cccd97c96a6a3fb60785024b927 GIT binary patch literal 3884 zcmcInX*iUB+rDR-7(}FOW0@?WzqMkdZN`?ZjJ2{yg)n0*V~v?4OURl%W2X#>kQ)50 zqtfu$*H9SQ!`PDN{(pOq_jsSKm6-j4Wa8CbyK@Udx=DFER4VW5Mt2(Vwy!F%w}i*@GuocM5; zaP%F&i1stn4C;$|?8Q@I`KP4b5lUoI4MjW)t+QgH*YbVMstv>aJgwZKYZOy812BoB zPYsOh3*Cy&930VVMJC493YR~v;jl>8%GPp<@D}dp=~^Z%jpBZ_U5%_>pV*)Y%7w1H zpMUvnemms%WtXK(T5rez{0$slgH4hiR7xQOLoi?4>(8e!j-k-qD!V)^^VjU>@87>C zCns+V4b8oL{P?kq(F+B;28+re_w!mhsaMI&%$S(B3ii%X#qOayB%*9$)g9%tZuBYhaq_RBZ6J}yp zXEjxd*Du`OdF`UJbW$NqDUoF1>oWe_$X-gCIFb^IIHzS64Uhf$yyvyGbet{2n1_uitiM zan_Ro(@b!OX?Qyy>}~m0L`jqWU>xSG&-LVHOExDWQi44ynhO)n3sg{-T6?$HZlFDW zg6~ zkZ2D2OB9+5rXdBOG>kM_Qf~k_x?V&~HrR@68PVxaX8_c_0xjLIV2i$N48=vxFQB3=W!A~IJM_`h$8HP|ZEuT>7dOJ}cV0>#103hm+kelsukIi^V2%#g$ zW-y0gTP!Ek>xeS|8-`7>WpNo|0G#775|9OqJNjVc!R%roypCXU9!hdYA@yWc0O|qt z3hzEzgiJ8O8%40QJByNItd6^0ingo{n<~`*kxMl}9vmDLb>Vhec zf$?Ucf0C&G`#7cb(8l!FD;3Ip z#2OiW=bol$^E9~Sp+A6p8_2N}(r8|C)6&ACZ8NH(j2XMW)_UBAY89obe0izo0z`9X zVccu(OUCUKQkF}Y3&VV(@IoZ|6lLgS^@KiwmlzJP!rRMVb3%g8lX@i7 z%NaB5#mf#B!fv1!#U%-#1fFoS6VL$8E8*UM29za}{2wHbZ14DU-xnu6L{iM4w<>IR znFY^X=NGh1j0_FU^vHoyGLjZOP_m=S@Z_X;^b>G?i(@%)g*s0-t&s^#?R;4Sr*MS9 zlw+D6JY;7&(if8|WbE-MRa(myFHH`&6FI&1EJJa^Q3YRUbZjf|G=K`|x-es$28`>c z3H{vA0&r_dZfzM#korU=)&BViV0=oZFY3G<)Jw|Fsg%J5ZiG=WpGyO5O0S^}n=1%i zKNMVDTKXa5+QtDr2XC7wrBYK(B`neC&#)6N@ba>nY_~SzU$ChTYENsGH8?&s*>?Xm z(ER71W5FoNU*oqcj|Yky~2YA@*;P)iv&a|0T{oH)DSHWgnDUj$m@}~q>)41 z7)OWC%4;Iv~gs}p5TC#QAxWS8adC==9}=L_|tAw z4~7?;4hoQvTxQ=fP=GQlBzYsi$&4W9U+e}Mk!c_UHU0kp(EYTNq$3!yb7*882i;Gq zt^mD?M*mY!V6m$y0ygzj{a2J+ifnUz<#hPxuEgZ_un-sYZgrk6FI^Ry7Cz@N(b^cv z)5;xms2vp9qpXzr7j^|ETe4=ocul7p* zn5zKYZ(*&fENq8fs7a@!edO1_g3rDr8}WkkxWdcRMS|*gx*{-czA=yr3ikGotkHE= zw}A2;^9mbh={7l!VB>9b!rnGJu90ojGki@%jr6n_D zVXZxIa$no*o9-{WsO(G1b}}t7T24xQ;yuaFucaxCBOOAoEHn5&@i$6TvR!3LHNniN zWpWwv3&X$&vxh2@U0W`oHDAK8HzCk4w{9dx^qB~7JUiBM{@pG^hr{T|wZ`&!!ON12 zKOkj%+B@zM(}a7A8)^O}Qf`M)}k=bkKb4o|w4 zq8DDu3B4nQbf2WqcD=nun+)eA5`IgEOrQ4WFRF_-#_wlTY+*D9^jm$eym(WbFTvi# zda}|oUG_+Dwl*>dnV<%R9!%u!ETY64cDsYgveiVZY>2xmloZUv^_PZu7=u=2_Ht zbR`;@464?dg?vvn@NjzgRebNI0Vr$9zP+`XAg9$_jz3~ZgSuzX;A+ziGAy$?ve6|8 z7^h3y)leUR)axG6{&wB~z}-ty8nrr4-q|kGB+?IXshQt(zhRKV%a4;Kp?zPyip4zwNp=F z|Cd8AsbTus@YLB#+)CUyCP$rD)Z?|!&^vCnk+=z#nZqsGhvI?HAEReHS4A}`=b#1? z>CyLp&|c68m%J4&_oA5L92Z}lr25JhA$0%BZfmX4-mFCpdMK1+eQ4aJ;90gOHiNAZ zyrt-5B56i|(6yncbTHlI~l4dTh zA~Mzj_n346<;}e)&*W}n)2PtO#<`sDo!4B6dkxmXEzjyGm5ayY#PTvx*Dn6Tm}mIo z%{#v&DQV0okM4aXFvM@|Xn+*cAY0&i_|FXUHBqaZn&vNzyrFftsS8n8mbX|OCfitB z9ztm+|C>R)zfBxlb-~{5r~JH^S0z#l-ugPx7X=?3yrdL-Iuo>ay{&l)OSfJ~55#5u zJ@d-f`WTzhouY0e&(RivEh4uF%CnM?9`}M*3uMO1R$xt{5_)IvSJjuZyxa+{z;fIanRlJ%p*I3g|vW#92uRZ}c4$-B&WF;OS#w z7B#rCvZ|=aAlhUj$t~G=@Vxu6Hp=mcF|HdsZPAI_BI=R1Tuzy+ik;AQ;;pmq1ioH| zWHfDMRkil5?b&l>@HIPL$cWOofMIvrq1tS+YXv2%{!zlISjW(N=&(oq<^gs3%IVz# z4c{CInsF0$c31MZ?v%9hy{+-v+=Sh@>9P=Vp43Qpx{&I5u%G3}3P@Ewo@Ja!4ZW4z z{dB&#YMpXH5&Cv6&gaF^O{&UzOZr(P?Csk^a9B`K&_j3kzN;eaBvLKh zmq@g-wA6Jnbe`-Hfax58g5EgpBA@-1TF7e>ab$k8xBYPft%@TK(u#i}>qS z#qZ5NlQMCOocEm+80>;c^ZZ7Rrr#%AU8iJRsLPYaKddo4BllX^@~9d!Z j%(KE`OW!k^4Q}9@$tU_5bHv% literal 0 HcmV?d00001 diff --git a/docs/img/autocomplete.png b/docs/img/autocomplete.png new file mode 100644 index 0000000000000000000000000000000000000000..406bada81d9b92c4a6bdc95a9e8ae50be5e2cbed GIT binary patch literal 2613 zcmaJ@X*kq<7oPc>$rxcUQ$v4qQ_XW4B3~mWqI0UU&_{m z!WcW*_bf$`WXMRCEY16WzP%sb>;3Xuuj^drocrA8J~1Xnx&laXBm@Ex(AOiIK_E~e z@ca+~fU8?|&@>+;(-O4sBV9 zPo<0BNrhn+_z!zbOnk@ek`io5Jc1jNTOPg|-%K`_CfAoAT%&nB;|4+ddX|2yJ>GM3 zUg_055pL&4gaA^o{X#+gZ~&#pkHf1A;byKvsH;aKf>A`u9YG?ig#LZ>l+;6C8ck3@ zAkR#xTGJ(po(AMoEzHe_M@Q3AQ=JZUNZN{~n0{YaC@d{4EiR6|m_v!pQbg479TtgU zGMU$pkM{Q``u`ymd>t;jl9!j~Hdg90DKR0FRZsNw-Tc~>&R$&1KkCEuOyVzpbnKdI zUnn4RGL99CITjfer9x0I(D_tCmQAu$3opQB)zhu-%cpcsQ3s>|IEkpu`aO|dF0((B zz5(dPZCr^5ctnX5Ag3jgaga!1Ja<52Ga!Q^Sb4SFddOh*9y*7`f+eQ06f0iTi6%Y| zPZe5;THY=)Sb0n|M$CbqELMeM`&^Q;Rn^PN%0WZEnkqk2lJrMdTU*=2{eZTQVSV8?R&vJG!`;Qx|whKZ3=E}E7~=sYIFG+LA5GT8F*bM zzNCb&?6p)&Qbwv9A?DB-drkC5*NupO(^&%0TzCNht3%@OHhKL*po6_ogdgVQ*XpW( z<+xx}mO`Q!>Pqm?+4E5I{m?QX6WVCdT_V1%O+1DFa=-UJ!8QF(a98hi1FsIEAH~`J zG55E?&UD$2ka@qa4~?qFD!kMhuR{g!=K5AdJifE!etOI0^~bIjw~xPAj#^aWQ+(Od z_NR{9n{dzAI&dem@9HQw+t9|6Lx=D|Biy>Odv=VT;oY`KYxS=@HG_Cd@rQdw(U}KK zYN{ZD02!WmAY1fdB?gvw0Is@U<|v(kdg_<@3P)!sX4xZ5p%G&(x#spgjtcvv^{m*wL=b=uqw~m3Je{$1q#05PhCg1U`QCNXQ!e8w7TaKf}0c?^NwtuvRzZst+J2I?CdJBv}H0Z1rZIKz!H$SXO2GiTKIpH@uVstB{7vAmBm?RRa;Tr^~+3RzjShL z^#^>3j<$$m23=d&wP$y4g>~2!pfZ$ZRPgZKP~IM1%_%)I)oO}o2Unq$u=l;NBMK|3 z<*#&8;pU-Qn)d#&pT=Zt%2_p~)iE0{{b25DqWW`Hj}y1z;u3uF{XR>7Z!_dFY~z16 zp{%Su++(9hjl5Ph9KS8!WycE`Vh2y(9ptP5d|(DggA5yg*`_BoKsn#nUqpF)>*817 zuWYdj47e#K=YhrIVQwcn29h{EQr zSk4r0Nt^N5{?f_un6Z~Z2g${%L58zxUjoANnsyz%xj#=kl%FX{$d;*9XlyHs<+(jN zUNfoAxM0@^1A-gm(GKTYPdq>pnzXE%Jx@4!c!>q|1uL{xe`@P$a{uk>xG5P})aW!j zef*F7xJzZ21M{Y2+0pAK#h0*v3JhRYSua-G{z+ZcOL?msE$U3>!fcNlZ-(>KM#mi_ zER}ikNq%Ta&dFZ(VJ95BbaIsxRloP6zfY|oq~vwMWN=!+H5WhPOb(9KFdd+=qpp^_ zZN*))qY)@LqJiDyU{!T@aZxglO-xcYxndaPva8C2fDw>`K7{z(BXowzW3SoA;E`*J7au4?ASYq5S@R&>lEo6_d9;N%P!$Z%k+}7a7`3m9hS0Jw(b7%u5mF zycIe0@Qjw(^zI9@fwq-D^Q`pFm=|{M0L`IfpK0&&7smu2T@hs&#Q)DUZq<15O%HB$ z1jBb7vT6P2|;jOCK(f**Y%}0n421u;G@cC4mTH56}z!U~U!$o4q>Jq&YOHa~% z{`~prlTGVWJ1mTWT41wNv^$=ropBp&aj5w5!$V9NkJJ@Lab$4D_xyi$Av$nQg0riB zQ(h|G@}naoXHK6^zH~&AkR=SfLw3M3>WVuG(HM-sL0l=SkRNyt^lswcKW-)>T#nPn zn5xgLQxB9w4yH&m+Iav}htj{wDf_Q-hH`<0fa^I;fm4Ry90<|ZF(Q|e?85#7qO`~~ literal 0 HcmV?d00001 diff --git a/docs/img/create_option.png b/docs/img/create_option.png new file mode 100644 index 0000000000000000000000000000000000000000..fda8ca33925468c8ba3eb3b9aa7de62954f30ef2 GIT binary patch literal 3762 zcmV;j4o&fiP)O`CJfw<}mAt#8dixEN+D-5N8 zfUF5HvbG+K2Lj0u0JS7T3(CryK#rvOckkXE93130E)WPv7Jy73ki`Tv8qMa-o9Szm zjS}k?DwXc;Zllq-cJ12S+}tdN0;T~0Ndj)S+v#-f*|R4~FcOg%^(s4d?9l7=AQefJ zkeT%a=;Wu~qOY$n%2Eln+`D&=dX=mv0tNzs7zC&U=t?BMT4FQ-hk$@g2++W4>7rf2 zx5xwsm-wkmKc?7{M634mIXB?jfQguu;UO3E1Nt16Rf0ty3xr;fnH#Kfgbm*{C178Vv16eM{5 z;9C*|1;w(5z|73dxpU`2_Rv4*+XQU`wIm1%ie(RhNQFcyC$@$Bf3JgDVk3g1*+$^v z#fvlyi?nJXDvNC@3H%4vEwK^7(QG5|S}>(x6(MurTTHp<_{q@)K!~s?Vffxj&)pF#>kr;L05D<_9fu%kJA~89PJJ^^R0-%=6 zaD-OmKmgPthfxL_GeZE>k{OQBiW~??uks@;|Mwko^RDMz9 z7Alob{xVUJcQ3m!fptKD68Pa?n)iQE5JNC=)Nk2t-LEYa=l^jVajrgb4hM)Z%wFn)Idyr~e6=4&4BO zBnhOX7PqZ}#RiLgY`!zUv%#cIccLo|bq=?q-l(Ll`p4{z^$m76C@0DBgyN5rKuT%} zp3uN&btfO`CBx_FciXMDjxi)E2NDqYnGi@-Ey}`eFQOJny>Jez*{GyDxJrZB>fjL9 zVKHbcn%ttrE^E2gVCmrKpJKK?oE?O*5Q%YivvJoa10&zRwSld$(RzM&OI^8MY*Jrd z*CIL`KW#{FvRJCL;eI+CO=dm)o27k1EEnVnDdPlERSQxY>#X(KrFt1_v6xS~E9^f_ z{$#IlpR}BGAVZZ=>9V_M+~#()xW>g7jw-O=w2#JODhI_JZmuF=eQD{AZ&Ewn(R(Q&dqTwrO-kOKtLA&|0K z5Mqs%#wB`jcUU|8<@I&tT2@Q)Zm9|upl;3WaEs0h5)jBR zfi%>DsDH6E)-Cccer}B8#=koJEDee<-tg9y5x<-B(+O?xI~|S=+nBbd#$xn4S{zRM z7+YR$h`%rqo1E(UX!{DW#r*xB4R9P6wfI*mm5BZX6aDS?!{$JSrwKF{B#@R`M7`97 z^wJXAA5*f_r)+!w+9a7zbb-MlDi`foQ)#l zlv-nzfxB>l(^nbTwFLEI zt>3vSe4cu9ldVqKXv$xKS3E0jc?BaRrM}!`C^S};>**V<$!sd3Z;aYyZBrU*EahC| zL35+a&zer!8cj}%fwq`$s_#H78$W(qwq-Z~1X3m-UZ_iBoXGn0p<@eA-#h0ZeSLjT zCpT@{^yaRx3yBm$h(HE>i%d|0nLq&40&@TeBuW6(k|-%C z2L!TKEwE^T$(?ZmS*wB%o_9hSdR*dqZay=#dxeM_)2rNec)Uq5I$Oi(^1af|Vc;|1-xX>A-{8DG&g)qyP=7$dmx6MWzD}W~V>^ z)DniKDq#Nbmck6Ty`?aTWK%`@Ix%VU6{xI?souh9Xr&p_-7lQ&A@_OdvT<`&t8sN1 z9_$hR_8?AK)q1AkW!&9Mc5lbuoae8pm{YIfK)-OhllX#FSLvCC{kXN2Z;RJdjGlZG z!iQgkj~A6PZxoVm|4Bw>W7LRs=%vGoLq4Hf&FmN#>i-@-&sY`U*wa8D3E#4Hx8l7L z-1+@n?Vsm9XeVqbbF2)h=8eoz+xh0*?ZrpHT4}tfv!Nmss88 zOXaF@X#rj^E%^q5HG-cbsmd+IKB3xUC6KUhQR7!PprIb#HY%Qv&n+|$DBdl^npLR! z5QD~1(FQcq&Yzw@MI}t_PG+MDP2U&JUKXy>Z>mtBUAq<4n=$|TSBk6gaWpLL_ayxqA9H0>3Je0UotjCheK&>7)B)Uck+)5RyP=WTsn^7u{>_Sy zO6a2u@QG@Cp_TvEix2K+-gpk@&5`Rp!k?UEN{#C)gC-YKi->zbX!hWvdl`KW8SE4O z*ctS7GC<3z0^F zbcNKF;iBj98iX{f@k$@LF7<_bH`@8*y<~b)ICYk{JwW^RGnHy$wavYEnXKB*99|O;{B)Qa5L!LNGa$5$KhfcY&K@g)MAZ^H ziO@P>$q5nnP&mXdF2NcvxiCPc9-^Lp5@4AkH5%#XkJ*H_3FPBQw+H2_5UW_!#+m|L znupqY$jC!<>$c!g;IfEY3uvvYZZ!2kd|hxj=pp&WK~|QC@z_H6C*Aun38=A7g^HiY z)5F4aKHjKCIvol;Af9+}5)H{-EJQ<2;TnhTkBWY>czsZxJQ7WQh=xYVpifjq4~IM+ z(m#uer~~Cu&wzMTe2v$|5}1(j^CggQBo;IwZYaNU1NEj}*|$MHrQ#LNf!n|9N6i}3Lei;SIqNLMEO>GQ<1kgY%mE15DxKBZw8 zhqmrj9HX(y;xz3eJ|D537p~D|NiykLmrzzox9P$&SfY2aHPY=xhf0`Pp0s&L?gV` zx;IQbblnn>Mux~V%TzuumRYqLR~oU7e%2~bz)L(nq+5?)eh!BNCtmzQGP-`)T7ae| z$RLj^w_sf!qQTF>owzvUhT_8Xl4b`9tijZS(InYB%^w;@tLO&WxEL3W3=>qyu)g4i z!7?c@BR*(Nr^7{la)m?Nw&P7|l$(dPZed^se^U%2XYUJV9y!fW|L`g!0+`-A>HXCw zkrr>C`|-O6hh@Y7Oa%f`1YTc#zBlK&$Ri0yVp7dP9uSZf0Z@yqh8v7civXx4EqqW} zRs=vTvKnqMHZ206mbCCeWmypbwa99?!PvA2fLhYR2bE<-0MsI@;Ra)qAb^?Z|B*|m zWtLtf3<(Ixp1_PE`d&o|wcHU_${rK|00QVo5^q78el|00LnGpca@4Kp;^9pq4~Q cK{+7sKV7E+0ceo*O#lD@07*qoM6N<$g1Lz1EC2ui literal 0 HcmV?d00001 diff --git a/docs/img/created_option.png b/docs/img/created_option.png new file mode 100644 index 0000000000000000000000000000000000000000..cb4eecc1edf7642cf77146ffce355cd67929b049 GIT binary patch literal 1625 zcmV-f2B!ImP)W|Lbq^`>%+B3woS} zHn1@I&>yagEXQ$Y&z`j?0SF5SG$-)(?b}MFQmt-x^?oarKOeZDucwSPm8z;N%U-#1 z1%%NY9@JWz0G{@E^4n}SEQG^A4Q*iE;EZAE@F6%La0&q|0lcR;t_{4$fQ)epIcT;z z0W1N8&~a^8J!*((QA?%}qL3_5fO0nkQ=3^at7Dnt1=>D$=l}#hC4m;Sq2#BB{o@I-c5ohOCp|g*U#B(MKl0?aC*gh) zdqp)d^Eh2pKqsGasKIxe1dQK|oZ73po=o``@41;%EkD8lRf?HHB{CBJh`!LJqY0pz zBvv@Llfl_6UJH3c@7=>)CW^__``mHpQj;^W zn?ruTe`q|Gku^e-663?u&lKIDoSPaRPe>X*)T>90>ixclYpR+UyE(H~Rkt2QhNttm zA5w~sr$(_y|LD}C{bYj5nW@pqM~`OpTK#(@ITtgIb33|wD4j{5ydIm4FC^E?h^UFb z#$yY~G@gSE1#6(UxA$l@{&=!5uyKppp!je!OgE}S$tPmVRoDDVcJ*gpHJw4Tt+cFe5&{_q-$c9!Fb0&`<2n@!zf zHu4g4V`r$9as`DAL<0R3W%q`hWT~Lbj36l1>k=4> z5j=xj5918^bOU#QC#rI_$(7>Nz`?*01gQRzxw)WixAdY)IBwsMdd;TTQ9T^h$q5u% z-EUwQc=sF)7iW?wSCp=MM4Gyb)UEiK|KJ;J_FcpZJ{!tOxTy5tL`X`kDTkBQ zH{K@~R8!GzY4vt+)v)WntbMpeC%Z|K=sxhIFEdZ}2(6YTpvk%ALX{x8iGQ{-D|y$w zLA%*3!|SM_p&?A;*!s$hPaE1eRL4@v5GP5|Jroz5U;OcdM1}+}y^>qc5_HJH3p9^X zxpji%qg?ZB_ra-Qmn2?ZEj}DPasfcFYBC$Y4zDlgW~WmxD9_Adl*L<*)dz9fzIexE zS%q0$y?V8EVy&*J43GP{gDIEP`o)@c26$~_xgggN-cm@pOdfBF2$FR;DP=?H@jC1z zBltLNb5pZ<%%wq+6p6D%NtSmAe$YdevKf63j{*%E&u1iyw-!CzHJ5(v zXM})2)4Y#wytQ_4iX?-iJ&O69NAigU}a?jln|N- z_1ERVxWg0XE2{P>bk*O%Gnd+4xHZszcjy5GEJfg_r+*59@WVHH&S*PtSc(J$0|d+o zfHut8Kuc#60Bv;k_(SyO1PpE9CnNZE3g%|e0tBp2084qid} z0s(UZSOWMBZins0vp<@q6=l1ttE;cC4_-K8P7hkFNdRYzSFc_vibB(uj%!2RBS*cS zmoHyFfBqa6SJoVVh{~J*&IdRvcs!n8irrsdH0+G$8|J;B1qgH~0k}`qp#u+LpGx3g Xh{F_3H^>VL00000NkvXXu0mjf_+AHD literal 0 HcmV?d00001 diff --git a/docs/img/mine.png b/docs/img/mine.png new file mode 100644 index 0000000000000000000000000000000000000000..7aeea600e515a67efeb465740e1cfc2f9000e4c5 GIT binary patch literal 2996 zcmb_edo&d47M~dtgN&J>oEVy64uz1n@i=1!V~}SjDuld}GG0YSW>JCTi;iiFDd+6JWZi6)V9fF12y34p9frst-pjpv#;Hif zUibv`F|x1Hep_RqvJYr2*iA)ULfK+8en&Y-6nab-!46KaIyaibk<+0un`n8HP0GT~ z&(5_s2J9_&(dR%Qq+HR1sW`0dRDu%)yPjao2COI0aafS~suGLEGNNpeDHO_;E4psP zsqy6Gul1{z+;Sn%f zzcDT#D~;F4;i6L=&Ya=mqbyt2#*O9DId=th9ArtTd01Q9=b%aq%0Ry{3sZL!$3lM_ z?=E^kG;$#^;@KVn_j45;BGs+4@$v*X5LpIM_0&OA3ED?7nv!q^I*EZeB>~bm`$zMm zxeW5x$AjHPCHuNtvij=t>d>0TZ50(2ipv>YzT??zlU9seLXVmM*KZ#FFBEH+u8kov z_lCt~v63+#Rl0m7Z)Ce#Fm4hAv3EA*WRS8(El=v({4DDgO0B2rP6YLL^@+XaMTe?G z;BwrHL6`G)w5HxtTGchsihB>)UZBf?fr0-1`EPK0guSQvV;U_IkPl?bXySCi#?zXo z8(Oi^LIFMYE150DWb2g*n!4dM(r9Q%24Wo(R@l00v5-e&B(u}PgeZy6D9MA~o>Lyc z)8EhN%hTd^2!Y;6=*FhlEuC;Z_Rc_0eXjBQx`m+lGhFXDazuz1eq^upUgeS_Gf2Vv zx~-dwg$wSRz~mqIym=XkGd&zVrRfL1ZD-*~Zo_d^7nZpQx5LdmBgA(-?I*G(?~X`` zMX}c-0sY)eA?Eo0%sK+4ldIYGiTj7Ky~Kn;yxRJDYUj{s=w7~^`@N>-=4R*b(M=C( z-+DA$lRRTCVsKc7jmB?r;KdM%mWGC8{a9Ni_QOQKNhS9#SBtE(?@7fX(; zZTCy37y8$6gD%$A)dl?le_bS-8Y(3p{A?0x zNrSq(yJKTx4X-RE=#c{ResxagO+0yc_6anq97uY6AI#z8sr0F2$~`c67215>Ll)22z)T=^cg8+uz>^vZJ>amj`G2e&Y8Oo@+&h4 z5x%R#Z~gE{c%BvznWcpX`JVvL{oUMq=4=3TPD%zKO`bgaGBdJYxWtv!A>)TZ@60VweoULvC z1iNU`6o8*u8had)d_djTv)89C95ISnkVD>Hnr@e2w8& z+7^XmhLz({0w7`NSXWGppqu_a5LP0>r^q9(BD7&B*N~6bkRZRv$GPb0)--3!$2*t- z!Ae+0#xP@U%%={u1xbCPQa^%qoeS>NIN7dqpqU$s*tM!gOu3%)h$#-S)!VguDN4nu z0MkaO7m9D{Jq*2k0|jnUdb`*6IwpO~$sq0vjHd(uh4L|lyde7VglAwp>l8*Ie_SPa zmUn!7b95h;aF;49nUj~7o}Mm6zF96vqevuNHOt&Jzm1tbsv!p!$;Wjl1THm-xWKDT zaeVTXC8-9l^U3?YN{esF#$VbHcI0MQ<~&&PrSRB zzM{Lk_ua<;_w1=@!hRqTo`1E6(qD^CQ4lXDkL-Tqwq{dhk_rD z$WLUNveZ90aF#M0XP>M@rjO1W2MfmG+#%(`mcbPZTR}-CKid4B>7k1b@AX7AzKSk2uTbn5)F%7$!7rXo0wkqvukV@76TBa((&z(UmXFB?87M z`&O{y$9NfQ1FtmO?Yj=(SE+6f zWj?n>u6l81srd-63l+iS$_1VX!KOg!PlcF62Or0a#<5TtIMh8I&PU@=-wKEJUGq1H z^K5MJ&$L5H7}L|P0jjcTP1nEeZizMc-0>UGMCTq7x?}WPe#+kR@B;B+@+Cu~2~IK+ zMuyN*keY_R?qV|S>_jiL^5`#TlUSk(M@MOg*1o`3!z?CuMk(?s%@t*3@2ZZ$OU~)6 z*d?DO3>|;iaBOvL6ACoHt@dlib2aO%#QYz?)eYY_e;N+Gmq{%Mx_7nmohzLgJ+ga> z+g$O=v1D~`uWa)iLRTPB>WA9C^gud{$wK2(QqAaJjNA1hb_dLUI?p==Nz+xCKd<)} z-qvilBX$+r-1N4fTt9wo#j{b-Jr)OqG5zy&@)pV?KhZvnF0Q5%A71xUSnW+`wZmtN zUccfZ96DHA7h)SaF{9B}-Ys>VPka3V!mNfdm1s6>Six|e;ZVbRaRb#rabC?*(^&C! zdRKznZsvkrP@hjPGRLr~)h(AYyR~Roydxz)rBLu{)(FDMrlMDl7w(4VAshF$W4od^ zFPrrGszM}Dv}hGuami<$87{BdYeBd0Rxo=z0KLkO1+i765(-_7L!AFhJx$&t0%4;6 zODNN3)sZNSJ`Fj7hHd=$dG-|&kKrD} zB6~`1?nn_v6xa^t=vs}Rcf{19`2+oyVq`$BNLxKQ(koAG;UbPTu>gj z^76#R#7w~;n5EU-#j(eJ*14*r@mI+(Dxh8UZx7#0m#EL9A#Yp52>v%5Kr**AtH*oX F_!qx@MqU5_ literal 0 HcmV?d00001 diff --git a/docs/img/view.png b/docs/img/view.png new file mode 100644 index 0000000000000000000000000000000000000000..bd9b40d0314dca20d716d0d6fdd9598702984013 GIT binary patch literal 9178 zcmYLPQ+Q_EvW=6DZQFLz;UBAGtD}x>+qOHlZQHihv8|hZPM>`r79Lj3nuSqw)%Ys( zhpZSJG&VF45D=V%I6wgi2;|q-J1-Q-*IgTKX7TF*YA+9fl8Dc;aClnC6>w7awftS7+FY2{M9o~20vN#LMIALeEF4x^wr74q?3h*g{SS(rq%f}Yv7QAmiDac^P|oC z(<>62joEnoS6!6{B~lEOI2b7ivadswPy9wV6%eP>+44v{MP6?1PXmKFE>Z>FP#=H* zG2z#Tm?V1aHbOhI1yaHAcxE%XB8Aea5#L5Bkb(bxB0dGhnT}DZ*W)J=j=s_EYA-Dv z@ho3k^jBUSY!(GX0;EXsZ79gaXe{Xw+rA(;-qBbRu~cRwXf$f2 z1~X;)gO88bOD%?3yq|^Fy@4=!qLI5rR!53OviD~zxBa0gt#XPbGZ*>!@Y!B3RQv?7 zzNbr7RT>SW`|l=gTB#<3?1zWP`8rdYO;+zOr&WT2f+ZXx8u`+_tp-Ezv5kcyA9Lta zvruzMoeBY(CP-{8H+_YS_J`w$=UZJ}=Vo+68R~V$3z2?+iMb-V*O!;WsqE~IGmD%6jooW-Hn2#niSw?rHCuv$^ zVZ3f9I4+00(djfC_6FhB`l_^An4K@QxaCq9caZE!kV%kfy;iek-sVC=pvuWuS?Avn zalYeChr^j2U&=#c=YT|YzpXZyk0sHeuya|i)_1r+^6d_cqq45v6%83m?2qaw0%Usi z#l^)>7c184P5r5s3xyIyN(REv>MleBlOB~r(yXkkl#DVnGq1rp32S_E)N_Qu9kOXu z>DB8R;4WF75{QR(`hshUhaPHCI*+TmK2Fw}s-83un0YTc8_buY%5A?q!2j`NP^HrW zpe|8{TxVV!8!P(?VhTssuh$goFKA4~5DAYzdS`sExX%@$Rkz?BRXYbzNy$j@vuTXq zT10Z8wHZ&O=?5oxt5j-*79>(Dk1{{Yx-B;!=n)0lHfUsD_T50&z9$<+9tr1QTJA9|Y#bz_R z>xE&k!Hls9kp^agUW&9^BI&;inRdSL^s2Pu;r)1zp5;Xa!<8?-3Dos|} z7O+KOH3#FVw_gx|m%2W~tms2bCHOPC8JkA!sHCc@ItpSTa1FH^fwCo^3kdxJk=e)& zRed1g2@tMX92PhPgu>HoRIcpl(UBPU=eqrrX||bWbK%{l`{hJ7fA%bj$IXHF-OiDw zb;tcdvhEE}WIVad$!15JQ@P{V{z&{+0ebzqaGJzl`9I(0zFbbH*=SQJh39zpXJ-CS zir*z_2Dc-1lH1j`VCRvq@QP$2^-OjLxT3{cW27*N6X{vHYPp&(8-q@}TIxUuO;8M@ z!l=Eo^B444*5fo|(mY*%``jtf@RCHNMN1a!6^UxT|9J{;Wu%&vhbF$LMH{4h8ckP1QExRfl^h+12)rf zrt@ZxP_^$@)})E~af*2T9?s<9*eKTg0j`f8hF+Yf-xqYZ6XLwl>PQIsW1&=;WD>X} zj46+g5~JX9wE+fmWz+G`wEhRpeSD=5aVyb`5z9Vo)#1?r1jt2ZTu#-Pp6rK zRj9ii1s92bhi4HM=vm36MnXFg^CF+80JBEpe-C5vfp`e};)+p`fPmsWs*Ldj+Txe(`;kkWT;IuTwK=hm`9nU?aRw41~7=}K{il7f%Ji@dDCln z7cc?i$IxH9lBk10bNzrVk6Rh9&z)d`;fIr@D(i_9d4lm2Tqt-Z3O`^UgN|Ly%~qwd z7!Wh*s6c@)aDjoqUZfkAc|6|$NM0D(oD*zx2>bkcU3-Ys;iG>*Qj;K%xDtQFt}Mq0 z=_ew>;8m|S+vaTpbN5Rj^YOCXsl#Jdi-S3)qDIbVmt3-0Z)W6=LJ?fN=<^p#fU%&; zf1QFzZ2#0iShI4H3R5oE{3j0tePyr<~j+9ywSNRp}Fg#NHkSBlWKkt2W_yj zaD5>Xq*Tfg#u=E5EkO(>1Ghm;FL?zLN>^Eg%9>zfidc7QM`7{k4U9^7m!44=0(Fno z_*^c9L@F|y2yf7DwasP(`AiPi&WT2U86vS=G#Zrvl%ys~kUdWw*8{ZT9>bw1JoVN^ zaD5j-|H2}~Fg_Q8MbcF4q)|ogJ^GQ0W1H=6U?Fnf$PEPf7|pgP$wnx4fU#39L#D0h zu7$J%`!4juHtt8P-#KklfsHf}$o#l0B5{+ERB5I>8xk^-V+=^>pDBUg0C@qr$hIL~hkEZ9SN6knmoradP9Ev1a``rl1hO7%6Nmi*lk(+LA`e(x9 zPF*z%8qE`l?bQf6;;TPx2~&9X3-b5tGg^p0>Fw`=dfnJcu|VAW4(*J>fimqLy|ks` z(t2}Nt`hh|5m^2WNT!T}@gOZ2MJJ$_)U^i}c5ag|X7{8^U7 z*ZwaRGl!z8D~LoVBy79e>m?|Eobs4jK(O_#WOSVsSWt3gnw3wmQhaZC)@*tGmU~d_ zmQ`cdW2O;*l`!jbfqgSmw75f)wZWq9z_};F)FbUDB1tG;+X>+C?*HhP_MQnYkq@i= z6h=SObq8G!5jG-L6RB(55_hj0_~3q$PBJ{hf&7}faotFB9n;-<$LoBFX|VBq!YA+Ii|Rdjwe;VI@HAGe9GXk z#_vHe8KnpCsH_q1|1`2w0UV%ri4sU=rVt*da{`c}^1cppVodYAs-*JSpFuR`1iT_h z5gTZlnuVh1>#P(uq#x{mPWgHza-i&VPIGGugY6@e`QqQ=Ka z>p_{+*D08Oi)9OC?Z@n(W(CkX-(^l)73C>IcpNQ^CsD&>NyCH24ZK20mbp{tEC!Te zyikU${4P|^9!_ki5!285Nz#O>*=P}pkn@My3L)gGL^3*o2%3%L%JJT6egz^zH2Mcq zdr2)WPIZc)9EN?|H;YiRLYv~F+tcmjkMA)2@(`i1SugR&t;(RoM& z>%NsOD4_N95kinFGzItrNYla96r(WHCd7A^H{5JZpjWz=ov-vdiN3i0b-6Jy(^5+0 zN&<*hB05v3)f3-nnjir3B7xvJu}}Rp&ZuZyCm3=P^{Igzz=TnAh8SZ`k{LC^F`&pH zbJ=ZNt%CDz3J_*3KhaZsIp6lM2P1KURey?_{aSa~4OC6SG6jR@P-kGE&XxDe>mH!V zYTpM%q6d#LQL`VIGyL{XG*VjQv58CviGi{1`gh>opW|`EJ-KAF4MBjIgyzM%jlX^; zeCeO4ph5MbL}Dv#G3DT7nh<1z#UjZ1e6U49RHOqC$Sjc!1WYBo( zA^3xNmxr*%zQOvfj`4^pP&yC_o%<@Q@0U2DNJqp*VrCz`COHFd?fnt_gJCM{;cb z1__c^fFvw2p%BX_9sDyW6^JOM@p=fv0}-dy4v`Zz7L&^Y6UW)p8pyem1P_40fu)L8 z!y(NQ4u(g2#BSy!d@w|PN%aF|iEz2v?m<<>@&k{rtd-F>DOoT2G2wTORB00)W{#*B zfy;&5g%Er-9wdxgY8#l?CZ5s%L&hk$R1>}-E1`@=%j}0OIB2=qrsWYku)dGagcpXh z&)iWf;V=ov9anz8wjXjFF@soz@l_5&J|Wa6E#1@6E^ehRI5=h+#x#_@`KAK}n@}8H zgtj_i)B$C>G1yLVA|5Zg_-^1ShAA85+ApUbKX4yWyKY~4E-XIi@&=B6Xfl&nw?k-_l5Ts+wj zR|GT0;iQQk-q8yhkj2bn9YACEVloIIpPBnPDe$oC0y{5eMZ~#-5fQ%x#%QY6%QMn# zD(XqKOksr)g1u~Rw+7PIFZP?4U6UPzf(t&m*#=H6Ckf3#8medX8wBao+c%jEC<^+1 zio_xLAp3YfP&&E>R11A;!C$0{c5TeyBa#ZyAwZ9HA#JPgD_3B*qY;58=(3{_-Zvq zJ^N)h*-3Peks991HJ7vEN>4>)T9L5Tj{@{Bn#0pD+vsb(oNCA)(;7{jpQvb&hFdIl z8Ns<2*%0;eGO!KjfMpcEhYgO-SeHun3u3pSpI(B*yGbZ_JBCV)JL;r3!`_=_uLvalkFK35+z@8)8fbJsTlH_AP?* zZDx_Z<*jKgF1iKjw3D{Fq&PY8+<~!RL>-3j!QbDv?ZL%iVLhD82yB=_9KOS}@>f{f+gxK{X zU@*Fwpe64jhj`^*GHjblL;zy4 zNQo(q?ysZ^H{-+n!NtY1HHXx5w^y5rHJLY}yYy zr@zAD22jJ@se`EGD~W&UL5gr6*bF&zZ?3k#?#yQbGu)OofQ~%Fe`TNTkn8v{SVJ!M9}mgd4%)#D>ZoCqnm-kR#|qE1A0 z+A=VA&63yUUJU9^QSyCN*4)HM(ebQ36MA@`!`bd3<3X`G&fqiJt=%o5Zhy~t;`d5{He2?o68qTrR6no3YTQx=gJS+glY_$=^^rp6p1V$KOYCs7?& z+R5zk55Zi`5SIj5@)Y(Uxav4`oLHtc0Lb&Fd`Ee?DPlSb>ozRQ8cws@kTgCEVT9+D zGMd+pd4p%ytFX`Fmp^9Hl^2d;#}n`cs7vk^(^)Nj?$4?;OBNrYE+r0k__cf9Xd8hI z32p}VF;)a^3L3PN^g8(Tu}HG1b*{~6f2@`VxU9!q&ATqtIMo5Y3|1dTbCHy%Oz_jT zVWD9TfUxa%U3$)im*mq}!P8bWqoZ?5JYnes$S4JPo_3efL*{HeXwJ^!iqi@`T!GnW zv4YsPUXZ0nhHUxw{>80Hk+zDC=j|H=I$5t8J>BYxEQxF`>c9@HyMpN&I5Z-$=j-yf zt}g3qvv z&}auStST!CRc8ze$fa$6cjp_2_je6m%%1euM7z56&4-cC#pG=J?dkO!b5ZHHqoe`( zbUBX3oZxnQzE5x*bZ6?_q~9#c1ln+`3+)$_9V_4k2@7rUa`(X5{+lAdWE@PZYw)b$cj8NB3%ft-yu?}wv){3=?BI}xQEKh2u%sPI~L1a(M zjcPYn7H%#h>SdStRhoe7;j+`MTbh$ZD(8Yj%hP1aE!P8OsqNNHk&vCh)`}LSsVjd2 zdkwZfEFTrg&_)l#OE%+&I5QgD&dyz`14S>*%KZ1#7boiGqrS}@(P>d+4;Qfmpr>dL zH^cHdeRp_*Gj0iVJs8nO)k(y_EN!0vvGqqe%8Tc>!gma_P`K|eGM}s9b%x_ArT6)CzP2-y^6-5i z46UjbI(0S=LXnUWrn;lbc`$fbmw39fUB-L}OVZD$oeghFTF6xg!TMHwWM!zg5h3D( zDk3C-R4WT_)m;`V#t${k)WY*6ftG1eD`>f~LR0MX4!B~f!J15f)ybV^gVV9WD zksB}F`b6x5dYi@;bc`c{ACj%RtR`RNYsS%;*sCkaDIa} z34uo7tGizEZ`#Fj=ssL&TYW$_?P0I?TCnp}E_%CetMt=Wg~_g%i85%N#wy6kFmqdC z60UQT>$D6hSAK)tc-?^kH;-I%d7ez9i}xI3&2S^6-LaJhOh-{!u1fPDqPKgNXMZM~ zFM%S^o-IB#EG(f6L6%=`)ywr}BZ--GrO;T{Nu*D1a58quD1VZfjn<%k6!HL2tgA zns?dq_%%cW#tyP|OEwPH=*GCe>we}nlm&BKzU`6G@z(&KzMwwR?(^SQ!Q==HR{Li= zTYT=k!1NF$f4&WOEr_{aoXF`#_f>-`n!A#!2`!51W`!vY^UjNSpzutRZ+N3TvtL!X zH>k{ExmyKlt#F$>J@M0vVcnp#8(7S#%ss42C`Iz6H)>1_SMD5ben`;0nc#IG>YxLG5rNuvnQRew}iaQLw+EJurU3u_?{l6_A$|2 z*pZwWK4!|6hioGKQstS&wkOajuCO-!huC&p*nFlkGenC2zF5P0xr)NiN_Uy@{5+a{ z1B%VG13OBZU9IAx7-p<(@to0JuKV1+EJ&G3?KS=r7QIZ{1BBzQDrJtRu1&LX){=h( z8xzgZSbj9xyocMCp+45=^&lun`o9rx*SF$QWcJQS6XqoPeT>^af77oPOGOh z^`dOkQ0^lb{sqBDx%R8;5Q>VPM_$(EimelV<8fhcvkn1{LJ0$;tLn*mjnzl_^U1x( zYhZ{ag_Wqz8gGk1qDK~%hZ&rv>sjylE7J@@?p~a5E!t?`wQ26=P@eQqY!wG}T$%KH zGBiND6(uJJg@cm$%&NS|u{0$?VL5ezhuHyhy+t8w9gPRho$YbPdvdc*Z9d`NxTzWA zvc_VF0d&k{)>BgUWoi55zR=`irKmJn5Fw2PqgW$Ht+Thle6dn3UjV%c)6Km#(JDsDt#yUFDS-O-WWoS~lBXrAVENHA>NXuhd+*IK@rbumA2>u=KN>P5@xM zEaqzPEWuZ_s#*Qv`6y_wLo;JG94r>)0+G?ELLV}00?S^((yCN_HRGy7Go3Ec=Gt@o zg^`UbbBGTG1^um~#!{;?duV(U0~-hDeS5Spdg!9~e&avnAjAvS@8X zyc?*3{AG}I`b=9Yp9)GbCT*g-$`d6V zp2-qmFiwFTcB2&)1*Zc?daLI8eoe}oQ;2EqPAr?C$|JDO0Du$(F#z(bgBL1BrRS);FtwC)ToY&>l<)@l(?G?5+ zPOdif++LEF2&2#_)X$Sy)s-*UT@XTIPiP1!csa;NMz&*MALF7qFcH$uP@k(egTms# z$sY?XX(*K;(;E;xN*!4K|?uix7+U@ zZ9QlqbhcWKoPEz?NV;zJy!92k;9dF2P?gPZX}SJh_UH8V(HN(Jf|S>0t2fICIXu+q zcjULF=}?@Vw4VFPYvE)xj=bDm`EtR9=}UfXS#HNFhpsvXpPq)d! z9dHby!LT=;02~=1Sk1(IXEUvwW%JwO8fZ zTVD?PSR&d-i{102=A&|WdjaHo$sSB5%&$NF*D-mL(Jw@M)b2(!bEY5ijb3Kl>gEwC zER20`#skpvSOn8vH&?5>$oWKk2n2u(o%tNMvefk>rjLHny33lHt%WGozG$(5C6o3{ zgOxE6UM*&{oAheBVM4F89ma2vli+Na5Y{*!8JezI(}K#^awAKtvF+F z=w~`8*Ul7GrFxaH=92$Jj*n+Vlr1X)OG(1c$O0NFmQ#4=KX6;(SZuZ*AnlZGN>Alm1g)6j zZ^7MPlGIvf>K882)1ac5dYY|Tf$3^-t?l#6t=aW^NH*I$Q*0O`_z!MIIFYx24Bhprx9+z zo(*qFax+U1&-ZeD70J>CU%K!o&zlRG{Zr!AO)TdC#Y!UqH^zlae2D!_581i?w}&*? zWU&t&%39KGXxr=giS|xni6$t%&SO2xg5o3RS$}TUl#yet%PT$CD=hfbpE@azI{W() zizRR|eEPKNy{%3T%Uj2x%Vj>t6B!8MsbVrxP$K1}dSkk1z1!m{_FR7_SyS!#zVGHE z=$F>5NwlAte0N#%DfgCro2xdt{uh(V?0-AZf96zviwJlQmYfr5{yQZ?i1Y(7##a&B z|6d*;&iAXM908JY`rF)ob;ht6vk?DE#K3}mAS9B&N4iFaWPA` works here too: we're relying on +Select2 and a QuerySet of Tag objects: + +.. code-block:: python + + from dal import autocomplete + + from tagging.models import Tag + + + class TagAutocomplete(autocomplete.Select2QuerySetView): + def get_queryset(self): + # Don't forget to filter out results depending on the visitor ! + if not self.request.user.is_authenticated(): + return Tag.objects.none() + + qs = Tag.objects.all() + + if self.q: + qs = qs.filter(name__istartswith=self.q) + + return qs + +.. note:: Don't forget to :ref:`register-view`. + +Form example +============ + +As usual, we need a backend-aware widget that will make only selected choices +to render initially, to avoid butchering the database. + +As we're using a QuerySet of Tag and Select2 in its "tag" appearance, we'll use +:py:class:`dal_select2_taggit.widgets.TaggitSelect2`. It is compatible with +the default form field created by the model field: ``TagField``. + +Example: + +.. code-block:: python + + class TestForm(autocomplete.FutureModelForm): + class Meta: + model = TestModel + fields = ('name',) + widgets = { + 'tags': autocomplete.TaggingSelect2( + 'your-taggit-autocomplete-url' + ) + } diff --git a/docs/taggit.rst b/docs/taggit.rst new file mode 100644 index 000000000..6d3919089 --- /dev/null +++ b/docs/taggit.rst @@ -0,0 +1,81 @@ +django-taggit TaggableManager +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Model example +============= + +Consider such a model, using `django-taggit +`_ to handle tags for a model: + +.. code-block:: python + + from django.db import models + + from taggit.managers import TaggableManager + + + class TestModel(models.Model): + name = models.CharField(max_length=200) + + tags = TaggableManager() + + def __str__(self): + return self.name + +View example +============ + +The :ref:`QuerySet view` works here too: we're relying on +Select2 and a QuerySet of Tag objects: + +.. code-block:: python + + from dal import autocomplete + + from taggit.models import Tag + + + class TagAutocomplete(autocomplete.Select2QuerySetView): + def get_queryset(self): + # Don't forget to filter out results depending on the visitor ! + if not self.request.user.is_authenticated(): + return Tag.objects.none() + + qs = Tag.objects.all() + + if self.q: + qs = qs.filter(name__istartswith=self.q) + + return qs + +Don't forget to :ref:`register-view`. + +.. note:: For more complex filtering, refer to official documentation for + the :django:label:`queryset-api`. + +Form example +============ + +As usual, we need a backend-aware widget that will make only selected choices +to render initially, to avoid butchering the database. + +As we're using a QuerySet of Tag and Select2 in its "tag" appearance, we'll use +:py:class:`~dal_select2_taggit.widgets.TaggitSelect2`. It is compatible with +the default form field created by the model field: TaggeableManager - which +actually inherits ``django.db.models.fields.Field`` and +``django.db.models.fields.related.RelatedField`` and **not** from +``django.db.models.Manager``. + +Example: + +.. code-block:: python + + class TestForm(autocomplete.FutureModelForm): + class Meta: + model = TestModel + fields = ('name',) + widgets = { + 'tags': autocomplete.TaggitSelect2( + 'your-taggit-autocomplete-url' + ) + } diff --git a/docs/tutorial.rst b/docs/tutorial.rst new file mode 100644 index 000000000..a641efbf1 --- /dev/null +++ b/docs/tutorial.rst @@ -0,0 +1,864 @@ +Tutorial +~~~~~~~~ + +.. _select2-tutorial: + +.. note:: **For demo links** to work, you need to run the :ref:`test project + ` on localhost. + +Overview +======== + +Autocompletes are based on 3 moving parts: + +- widget compatible with the model field, does the initial rendering, +- javascript widget initialization code, to trigger the autocomplete, +- and a view used by the widget script to get results from. + +.. _queryset-view: + +Create an autocomplete view +=========================== + +- Example source code: `test_project/select2_foreign_key + `_ +- Live demo: `/select2_foreign_key/test-autocomplete/?q=test + `_ + +The only purpose of the autocomplete view is to serve relevant suggestions for +the widget to propose to the user. DAL leverages Django's `class based views +`_ +and `Mixins `_ to for code reuse. + +.. note:: Do **not** miss the `Classy Class-Based Views + `_ website which helps a lot to work with + class-based views in general. + +In this tutorial, we'll first learn to make autocompletes backed by a +`QuerySet`. Suppose we have a Country `Model` which we want to provide a +`Select2 `_ autocomplete widget for in a form. If a +users types an "f" it would propose "Fiji", "Finland" and "France", to +authenticated users only: + +.. image:: img/autocomplete.png + +The base view for this is :py:class:`~dal_select2.views.Select2QuerySetView`. + +.. code-block:: python + + from dal import autocomplete + + from your_countries_app.models import Country + + + class CountryAutocomplete(autocomplete.Select2QuerySetView): + def get_queryset(self): + # Don't forget to filter out results depending on the visitor ! + if not self.request.user.is_authenticated: + return Country.objects.none() + + qs = Country.objects.all() + + if self.q: + qs = qs.filter(name__istartswith=self.q) + + return qs + +.. note:: For more complex filtering, refer to official documentation for + the :django:label:`queryset-api`. + +.. _register-view: + +Register the autocomplete view +============================== + +Create a :django:label:`named url` for the view, ie: + +.. code-block:: python + + from your_countries_app.views import CountryAutocomplete + + urlpatterns = [ + url( + r'^country-autocomplete/$', + CountryAutocomplete.as_view(), + name='country-autocomplete', + ), + ] + +Ensure that the url can be reversed, ie:: + + ./manage.py shell + In [1]: from django.urls import reverse + In [2]: #older django versions: from django.core.urlresolvers import reverse + + In [3]: reverse('country-autocomplete') + Out[2]: u'/country-autocomplete/' + +.. danger:: As you might have noticed, we have just exposed data through a + public URL. Please don't forget to do proper permission checks in + get_queryset. + +Use the view in a Form widget +============================= + +Before you begin +---------------- + +You should be able to open the view at this point: + +.. image:: img/view.png + +We can now use the autocomplete view our Person form, for its ``birth_country`` +field that's a ``ForeignKey``. So, we're going to :django:label:`override the +default ModelForm fields`, to use a +widget to select a Model with Select2, in our case by passing the name of the +url we have just registered to :py:class:`~dal_select2.widgets.ModelSelect2`. + +Set form widgets +---------------- + +Setup your autocomplete form widget for this view in the ``Form.Meta.widgets`` +dict: + +.. code-block:: python + + from dal import autocomplete + + from django import forms + + + class PersonForm(forms.ModelForm): + class Meta: + model = Person + fields = ('__all__') + widgets = { + 'birth_country': autocomplete.ModelSelect2(url='country-autocomplete') + } + +If we need the country autocomplete view for a widget used for a ManyToMany +relation instead of a ForeignKey, with a model like that: + +.. code-block:: python + + class Person(models.Model): + visited_countries = models.ManyToManyField('your_countries_app.country') + +Then we would use the :py:class:`~dal_select2.widgets.ModelSelect2Multiple` +widget, ie.: + +.. code-block:: python + + widgets = { + 'visited_countries': autocomplete.ModelSelect2Multiple(url='country-autocomplete') + } + +.. danger:: If you declare a form field instead of just the widget, Django + admin won't add the "add" and "edit" button next to the autocomplete field + for single model choice. If you want to both declare your field and have + Django admin's add / remove buttons then you can use the following method + with djhacker. + +.. _djhacker: + +Automation with djhacker +------------------------ + +- Example source code: `test_project/select2_djhacker_formfield + `_ +- Live demo: `/admin/select2_djhacker_formfield/tmodel/add/ + `_ + +.. code-block:: python + + import djhacker # don't forget to pip install djhacker + from django import forms + djhacker.formfield( + Person.birth_country, + forms.ModelChoiceField, + widget=autocomplete.ModelSelect2(url='country-autocomplete') + ) + +The above example demonstrates how to integrate your autocomplete view and form +field automatically throughout Django without having to define custom model +forms all the time. + +Passing options to select2 +========================== + +`Select2 +`_ supports a bunch of `options +`_. These options may be +`set in data-* attributes +`_. For example: + +.. code-block:: python + + # Instanciate a widget with a bunch of options for select2: + autocomplete.ModelSelect2( + url='select2_fk', + attrs={ + # Set some placeholder + 'data-placeholder': 'Autocomplete ...', + # Only trigger autocompletion after 3 characters have been typed + 'data-minimum-input-length': 3, + }, + ) + +.. note:: Setting a placeholder will result in generation of an an empty + ``option`` tag, which select2 requires. + +Using autocompletes in the admin +================================ + +.. note:: If using :ref:`djhacker`, you can skip this section: your + autocomplete should already be working in the admin. + +We can make ModelAdmin to :django:label:`use our +form`, ie: + +.. code-block:: python + + from django.contrib import admin + + from your_person_app.models import Person + from your_person_app.forms import PersonForm + + + class PersonAdmin(admin.ModelAdmin): + form = PersonForm + admin.site.register(Person, PersonAdmin) + +Note that this also works with inlines, ie: + +.. code-block:: python + + class PersonInline(admin.TabularInline): + model = Person + form = PersonForm + +Using autocompletes outside the admin +===================================== + +- Example source code: `test_project/select2_outside_admin + `_, +- Live demo: `/select2_outside_admin/ + `_. + +Ensure that jquery is loaded before ``{{ form.media }}``: + +.. literalinclude:: ../test_project/select2_outside_admin/templates/select2_outside_admin.html + +Displaying results using custom HTML +==================================== + +You can display custom HTML code for results by setting the ``data-html`` +attribute on your widget and overriding the view ``get_result_label()`` method +to return HTML code. + +.. code-block:: python + + from django.utils.html import format_html + + class CountryAutocomplete(autocomplete.Select2QuerySetView): + def get_result_label(self, result): + return format_html(' {}', result.name, result.name) + + + class PersonForm(forms.ModelForm): + class Meta: + widgets = { + 'birth_country': autocomplete.ModelSelect2( + url='country-autocomplete', + attrs={'data-html': True} + ) + } + +.. note:: Take care to escape anything you put in HTML code to avoid XSS attacks + when displaying data that may have been input by a user! `format_html` helps. + +Displaying selected result differently than in list +=================================================== + +You can display selected result in different way than results in list by overriding +the view ``get_selected_result_label()`` method. + +.. code-block:: python + + class CountryAutocomplete(autocomplete.Select2QuerySetView): + def get_result_label(self, item): + return item.full_name + + def get_selected_result_label(self, item): + return item.short_name + +Setting the ``data-html`` attribute affects both selected result and results in list. +If you want to enable HTML separately set ``data-selected-html`` or ``data-result-html`` +attribute respectively. + +Overriding javascript code +========================== + +We need javascript initialization for the widget both when: + +- The page is loaded. +- A widget is dynamically added, i.e. with formsets. + +This is handled by ``autocomplete_light.js``, which is going to trigger an event +called ``dal-init-function`` on the document when Django Autocomplete Light has +initialized. At this point you can simply call ``yl.registerFunction()`` to register +your custom function. + +``yl.registerFunction()`` takes two arguments ``name`` and ``func``. The first argument +``name`` is the name of name the function. It should be the same as the value of your widget +``autocomplete_function`` property which in turn is the value of the +``data-autocomplete-light-function`` HTML attribute on your input or select field. +The second argument ``func`` is the callback function to be run by Django Autocomplete +Light when it initializes your input autocomplete. + +``autocomplete_light.js`` also keeps track of initialized elements to prevent +double-initialization. + +Take ``dal_select2`` for example, it is initialized by +``dal_select2/static/autocomplete_light/select2.js`` as such: + +.. code-block:: javascript + + document.addEventListener('dal-init-function', function () { + yl.registerFunction( 'select2', function ($, element) { + // autocomplete function here + }); + }) + +This example defines an anonymous function as a callback that will be called when Django +Autocomplete Light initializes your autocomplete input field. It will also be called when +any new field is added, such as a inline formset. The function will be called for any +element with an attribute ``data-autocomplete-light-function`` value that is the same as +the function name. + +When Django Autocomplete Light calls your function two arguments are passed in. The +first is the ``django.jQuery`` object. This is done since your function may not have +access to ``django.jQuery`` in the lexical environment when the function was placed into +memory. This of course causes your function to not have access to jQuery, which may be +a problem. + +The second argument is the input field DOM element. You can get the jQuery object by +simply calling ``var $element = $(element);`` inside your function. + +So, you can replace the default callback by doing two things: + +- change the Widget's :py:attr:`dal.widgets.WidgetMixin.autocomplete_function` attribute. +- Register your custom function with ``yl.registerFunction()`` after the ``dal-init-function`` + event has been called. + +Example widget: + +.. code-block:: python + + class YourWidget(ModelSelect2): + autocomplete_function = 'your_autocomplete_function' + +Example script: + +.. code-block:: javascript + + document.addEventListener('dal-init-function', function () { + yl.registerFunction( 'your_autocomplete_function', function ($, element) { + var $element = $(element); + // autocomplete function here + }); + }) + +Listening for the initialization of a specific input +==================================================== + +To know when a specific dal input has been initialized, we can listen for the event +``dal-element-initialized``. + +Example opening and setting focus on a dal input after initialization: + +.. code-block:: javascript + + $(document).on("dal-element-initialized", function (e) { + if (e.detail.element.id === "my_dal_element_id") { + $("#my_dal_element_id").select2("open").trigger("focus"); + } + }); + +Creation of new choices in the autocomplete form +================================================ + +- Example source code: `test_project/select2_one_to_one + `_, +- Live demo: `/admin/select2_one_to_one/tmodel/add/ + `_, + +The view may provide an extra option when it can't find any result matching the +user input. That option would have the label ``Create "query"``, where +``query`` is the content of the input and corresponds to what the user typed +in. As such: + +.. image:: img/create_option.png + +This allows the user to create objects on the fly from within the AJAX +widget. When the user selects that option, the autocomplete script will make a +POST request to the view. It should create the object and return the pk, so the +item will then be added just as if it already had a PK: + +.. image:: img/created_option.png + +To enable this, first the view must know how to create an object given only +``self.q``, which is the variable containing the user input in the view. Set +the ``create_field`` view option to enable creation of new objects from within +the autocomplete user interface, ie: + +.. code-block:: python + + urlpatterns = [ + url( + r'^country-autocomplete/$', + CountryAutocomplete.as_view(create_field='name', validate_create=True), + name='country-autocomplete', + ), + ] + +This way, the option 'Create "Tibet"' will be available if a user inputs +"Tibet" for example. When the user clicks it, it will make the post request to +the view which will do ``Country.objects.create(name='Tibet')``. It will be +included in the server response so that the script can add it to the widget. + +By activating ``valide_create=True``, a full_clean will be run on the +create_field, thus validating all the validators on the field. + +Note that creating objects is allowed to logged-in users with ``add`` permission +on the resource. If you want to grant ``add`` permission to a user, you have to +explicitly set it with something like: + +.. code-block:: python + + permission = Permission.objects.get(name='Can add your-model-name') + user.user_permissions.add(permission) + +Note that the above applies for new objects that only require one field. For more +complex objects, `django-addanother `_ +should be considered. With Django Add-Another, a "+" icon is rendered next to the +search widget. When clicking this button, an object can be added inside a popup. +Once saved, the popup will close and the newly added object will be selected +in the widget. + +Filtering results based on the value of other fields in the form +================================================================ + +- Example source code: `test_project/linked_data + `_. +- Live demo: `Admin / Linked Data / Add + `_. + +In the live demo, create a TestModel with ``owner=None``, and another with +``owner=test`` (test being the user you log in with). Then, in in a new form, +you'll see both options if you leave the owner select empty: + +.. image:: img/all.png + +But if you select ``test`` as an owner, and open the autocomplete again, you'll +only see the option with ``owner=test``: + +.. image:: img/mine.png + +Let's say we want to add a "Continent" choice field in the form, and filter the +countries based on the value on this field. We then need the widget to pass the +value of the continent field to the view when it fetches data. We can use the +``forward`` widget argument to do this: + +.. code-block:: python + + class PersonForm(forms.ModelForm): + continent = forms.ChoiceField(choices=CONTINENT_CHOICES) + + class Meta: + model = Person + fields = ('__all__') + widgets = { + 'birth_country': autocomplete.ModelSelect2(url='country-autocomplete', + forward=['continent']) + } + +DAL's Select2 configuration script will get the value fo the form field named +``'continent'`` and add it to the autocomplete HTTP query. This will pass the +value for the "continent" form field in the AJAX request, and we can then +filter as such in the view: + +.. code-block:: python + + class CountryAutocomplete(autocomplete.Select2QuerySetView): + def get_queryset(self): + if not self.request.user.is_authenticated: + return Country.objects.none() + + qs = Country.objects.all() + + continent = self.forwarded.get('continent', None) + + if continent: + qs = qs.filter(continent=continent) + + if self.q: + qs = qs.filter(name__istartswith=self.q) + + return qs + +Types of forwarded values +------------------------- + +There are three possible types of value which you can get from +``self.forwarded`` field: boolean, string or list of strings. DAL forward JS +applies the following rules when figuring out which type to use when you forward +particular field: + +- if there is only one field in the form or subform with given name and this + field is a checkbox without ``value`` HTML-attribute, then a boolean value + indicating if this checkbox is checked is forwarded; +- if there is only one field in the form or subform with given name and it has + ``multiple`` HTML-attribute, then this field is forwarded as a list of + strings, containing values from this field. +- if there are one or more fields in the form with given name and all of them + are checkboxes with HTML-attribute ``value`` set, then the list of strings + containing checked checkboxes is forwarded. +- Otherwise field value forwarded as a string. + +Renaming forwarded values +------------------------- +- Example source code: `test_project/rename_forward + `_. +- Live demo: `Admin / Rename Forward/ Add + `_. + +Let's assume that you have the following form using linked autocomplete fields: + +.. code-block:: python + + class ShippingForm(forms.Form): + src_continent = forms.ModelChoiceField( + queryset=Continent.objects.all(), + widget=autocomplete.ModelSelect2(url='continent-autocomplete')) + src_country = forms.ModelChoiceField( + queryset=Country.objects.all(), + widget=autocomplete.ModelSelect2( + url='country-autocomplete', + forward=('src_continent',))) + +And the following autocomplete view for country: + +.. code-block:: python + + class CountryAutocomplete(autocomplete.Select2QuerySetView): + def get_queryset(self): + if not self.request.is_authenticated: + return Country.objects.none() + + qs = Country.objects.all() + + continent = self.forwarded.get('continent', None) + + if continent: + qs = qs.filter(continent=continent) + + if self.q: + qs = qs.filter(name__istartswith=self.q) + + return qs + +You cannot use this autocomplete view together with your form because the name +forwarded from the form differs from the name that autocomplete view expects. + +You can rename forwarded fields using class-based forward declaration to pass +`src_continent` value as `continent`: + +.. code-block:: python + + from dal import forward + + class ShippingForm(forms.Form): + src_continent = forms.ModelChoiceField( + queryset=Continent.objects.all(), + widget=autocomplete.ModelSelect2(url='continent-autocomplete')) + src_country = forms.ModelChoiceField( + queryset=Country.objects.all(), + widget=autocomplete.ModelSelect2( + url='country-autocomplete', + forward=(forward.Field('src_continent', 'continent'),))) + + +Of course, you can mix up string-based and class-based forwarding declarations: + +.. code-block:: python + + some_field = forms.ModelChoiceField( + queryset=SomeModel.objects.all(), + widget=autocomplete.ModelSelect2( + url='some-autocomplete', + forward=( + 'f1', # String based declaration + forward.Field('f2'), # Works the same way as above declaration + forward.Field('f3', 'field3'), # With rename + forward.Const(42, 'f4') # Constant forwarding (see below) + ) + +Forwarding arbitrary constant values +------------------------------------ + +The other thing you can do with class-based forwarding declaration is to +forward an arbitrary constant without adding extra hidden fields to +your form. + +.. code-block:: python + + from dal import forward + + class EuropeanShippingForm(forms.Form): + src_country = forms.ModelChoiceField( + queryset=Country.objects.all(), + widget=autocomplete.ModelSelect2( + url='country-autocomplete', + forward=(forward.Const('europe', 'continent'),))) + +For `src_country` field "europe" will always be forwarded as `continent` value. + +Forwarding own selected value +----------------------------- + +Quite often (especially in multiselect) you may want to exclude value which is +already selected from autocomplete dropdown. Usually it can be done by +forwarding a field by name. The forward argument expects a tuple, +so don't forget the trailing comma if the tuple only has one element. + + +.. code-block:: python + + from dal import forward + + class SomeForm(forms.Form): + countries = forms.ModelMultipleChoiceField( + queryset=Country.objects.all(), + widget=autocomplete.ModelSelect2Multiple( + url='country-autocomplete', + forward=("countries", ) + +For this special case DAL provides a shortcut named ``Self()``. + + +.. code-block:: python + + from dal import forward + + class SomeForm(forms.Form): + countries = forms.ModelMultipleChoiceField( + queryset=Country.objects.all(), + widget=autocomplete.ModelSelect2Multiple( + url='country-autocomplete', + forward=(forward.Self(),) + + +In this case the value from ``countries`` will be available from autocomplete +view as ``self.forwarded['self']``. Of course, you can customize destination +name by passing ``dst`` parameter to ``Self`` constructor. + +Customizing forwarding logic +---------------------------- + +DAL tries hard to reasonably forward any standard HTML form field. For some +non-standard fields DAL logic could be not good enough. For these cases DAL +provides a way to customize forwarding logic using JS callbacks. You can +register JS forward handler on your page: + +.. code-block::javascript + + // autocompleteElem here is an HTML element for autocomplete field + yl.registerForwardHandler("my_awesome_handler", function (autocompleteElem) { + return doSomeMagicAndGetValueFromSpace(); + }); + +Then you should add forward declaration to your field as follows: + +.. code-block:: python + + from dal import forward + + class ShippingForm(forms.Form): + country = forms.ModelChoiceField( + queryset=Country.objects.all(), + widget=autocomplete.ModelSelect2( + url='country-autocomplete', + forward=(forward.JavaScript('my_awesome_handler', 'magic_number'),))) + +In this case the value returned from your registered handler will be forwarded +to autocomplete view as ``magic_number``. + +Building blocks for custom logic +================================ + +Javascript logic for forwarding field values is a bit sophisticated. In order +to forward field value DAL searches for the field considering form prefixes and +then decides how to forward it to the server (should it be list, string or +boolean value). When you implement your own logic for forwarding you may want +to reuse this logic from DAL. + +For this purpose DAL provides two JS functions: + +- ``getFieldRelativeTo(element, name)`` - get field by ``name`` relative to + this autocomplete field just like DAL does when forwarding a field. +- ``getValueFromField(field)`` - get value to forward from ``field`` just like + DAL does when forwarding a field. + +For the purpose of understanding the logic: you can implement forwarding of +some standard field by yourself as follows (you probably should never write this +code yourself): + +.. code-block::javascript + + yl.registerForwardHandler("poormans_field_forward", + function (elem) { + return yl.getValueFromField( + yl.getFieldRelativeTo(elem, "some_field")); + }); + + +Clearing autocomplete on forward field change +--------------------------------------------- + +You can use the ``$.getFormPrefix()`` jQuery plugin used by DAL to clear the +``birth_country`` autocomplete widget from the above example when the +``continent`` field changes with such a snippet: + +.. code-block:: javascript + + $(document).ready(function() { + // Bind on continent field change + $(':input[name$=continent]').on('change', function() { + // Get the field prefix, ie. if this comes from a formset form + var prefix = $(this).getFormPrefix(); + + // Clear the autocomplete with the same prefix + $(':input[name=' + prefix + 'birth_country]').val(null).trigger('change'); + }); + }); + +To autoload the script with the form, you can use `Form.Media +`_. + +Autocompleting based on a List of Strings +========================================= + +Sometimes it is useful to specify autocomplete choices based on a list +of strings rather than a QuerySet. This can be achieved with the +:py:class:`~dal_select2.views.Select2ListView` class: + +.. code-block:: python + + class CountryAutocompleteFromList(autocomplete.Select2ListView): + def get_list(self): + return ['France', 'Fiji', 'Finland', 'Switzerland'] + +This class can then be registered as in the previous example. Suppose +we register it under URL 'country-list-autocomplete'. We can then a +create a ListSelect2 widget with: + +.. code-block:: python + + widget = autocomplete.ListSelect2(url='country-list-autocomplete') + +With this in place, if a user types the letter ``f``' in the widget, choices +'France', 'Fiji', and 'Finland' would be offered. Like the Select2QuerySetView, +the Select2ListView is case insensitive. + +Two fields are provided, :py:class:`~dal_select2.fields.Select2ListChoiceField`, +:py:class:`~dal_select2.fields.Select2ListCreateChoiceField` that can be used to +make it easier to avoid problems when using Select2ListView. For example: + +.. code-block:: python + + def get_choice_list(): + return ['France', 'Fiji', 'Finland', 'Switzerland'] + + + class CountryForm(forms.ModelForm): + country = autocomplete.Select2ListChoiceField( + choice_list=get_choice_list, + widget=autocomplete.ListSelect2(url='country-list-autocomplete') + ) + +By default, the selections in Select2ListView can map directly to a list, +resulting in the same text and value for each option. + +To define your own values for each selection, provide a list-of-lists or +list-of-tuples for the Select2ListView choice_list. For example: + +.. code-block:: python + + class CountryAutocompleteFromList(autocomplete.Select2ListView): + def get_list(self): + return [ + ['France_value', 'France'], + ['Fiji_value', 'Fiji'], + ['Finland_value', 'Finland'], + ['Switzerland_value', 'Switzerland'] + ] + + + def get_choice_list(): + return [ + ['France_value', 'France'], + ['Fiji_value', 'Fiji'], + ['Finland_value', 'Finland'], + ['Switzerland_value', 'Switzerland'] + ] + + + class CountryForm(forms.ModelForm): + country = autocomplete.Select2ListChoiceField( + choice_list=get_choice_list, + widget=autocomplete.ListSelect2(url='country-list-autocomplete') + ) + +``Select2ListCreateChoiceField`` allows you to provide custom +text from a Select2List widget and should be used if you define +``Select2ListViewAutocomplete.create``. + +It is better to use the same source for +``Select2ListViewAutocomplete.get_list`` in your view and the +``Select2ListChoiceField choice_list`` kwarg to avoid unexpected behavior. + + +An opt-group version is available in a similar fashion by inheriting +Select2GroupListView. For example: + +.. code-block:: python + + class CountryAutocompleteFromList(autocomplete.Select2GroupListView): + def get_list(self): + return [ + (None, ['Mars Colony',]), + ("Country", ['France', 'Fiji', 'Finland', 'Switzerland']) + ] + +As with Select2ListView, for opt-groups with specified values, provide a +list-of-lists or list-of-tuples to the Select2GroupListView get_list method. +For example: + +.. code-block:: python + + class CountryAutocompleteFromList(autocomplete.Select2GroupListView): + def get_list(self): + return [ + ([None, None], [['Mars_colony_value', 'Mars Colony']]), + ( + ['Country_value', 'Country'], + [ + ['France_value', 'France'], + ['Fiji_value', 'Fiji'], + ['Finland_value', 'Finland'], + ['Switzerland_value', 'Switzerland'] + ] + ) + ] + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..7848c8184 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2400 @@ +{ + "name": "django-autocomplete-light", + "version": "3.5.1", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "django-autocomplete-light", + "version": "3.5.1", + "license": "MIT", + "dependencies": { + "semver": "latest" + }, + "devDependencies": { + "concat": "^1.0.3", + "copyfiles": "^2.4.1", + "npm-run-all": "^4.1.5", + "select2": "^4.1.0-rc.0", + "uglify-js": "^3.14.3" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/concat": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/concat/-/concat-1.0.3.tgz", + "integrity": "sha1-QPM1MInWVGdpXLGIa0Xt1jfYzKg=", + "dev": true, + "dependencies": { + "commander": "^2.9.0" + }, + "bin": { + "concat": "bin/concat" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "node_modules/copyfiles": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/copyfiles/-/copyfiles-2.4.1.tgz", + "integrity": "sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg==", + "dev": true, + "dependencies": { + "glob": "^7.0.5", + "minimatch": "^3.0.3", + "mkdirp": "^1.0.4", + "noms": "0.0.0", + "through2": "^2.0.1", + "untildify": "^4.0.0", + "yargs": "^16.1.0" + }, + "bin": { + "copyfiles": "copyfiles", + "copyup": "copyfiles" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/cross-spawn/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "dependencies": { + "object-keys": "^1.0.12" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", + "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-symbols": "^1.0.2", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.1", + "is-string": "^1.0.7", + "is-weakref": "^1.0.1", + "object-inspect": "^1.11.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", + "dev": true + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", + "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", + "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number-object": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", + "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", + "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.1.tgz", + "integrity": "sha512-b2jKc2pQZjaeFYWEf7ScFj+Be1I+PXmlu572Q8coTXZ+LD/QQZ7ShPMst8h16riVgyXTQwUsFEl74mDvc/3MHQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=", + "dev": true, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node_modules/noms": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/noms/-/noms-0.0.0.tgz", + "integrity": "sha1-2o69nzr51nYJGbJ9nNyAkqczKFk=", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "readable-stream": "~1.0.31" + } + }, + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/npm-run-all": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", + "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "memorystream": "^0.3.1", + "minimatch": "^3.0.4", + "pidtree": "^0.3.0", + "read-pkg": "^3.0.0", + "shell-quote": "^1.6.1", + "string.prototype.padend": "^3.0.0" + }, + "bin": { + "npm-run-all": "bin/npm-run-all/index.js", + "run-p": "bin/run-p/index.js", + "run-s": "bin/run-s/index.js" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/object-inspect": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", + "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pidtree": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", + "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", + "dev": true, + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "node_modules/read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "dependencies": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "dependencies": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/select2": { + "version": "4.1.0-rc.0", + "resolved": "https://registry.npmjs.org/select2/-/select2-4.1.0-rc.0.tgz", + "integrity": "sha512-Hr9TdhyHCZUtwznEH2CBf7967mEM0idtJ5nMtjvk3Up5tPukOLXbHUNmh10oRfeNIhj+3GD3niu+g6sVK+gK0A==", + "dev": true + }, + "node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shell-quote": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz", + "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==", + "dev": true + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", + "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==", + "dev": true + }, + "node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string.prototype.padend": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.3.tgz", + "integrity": "sha512-jNIIeokznm8SD/TZISQsZKYu7RJyheFNt84DUPrh482GC8RVp2MKqm2O5oBRdGxbDQoXrhhWtPIWQOiy20svUg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/through2/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "node_modules/through2/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/through2/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/uglify-js": { + "version": "3.14.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.3.tgz", + "integrity": "sha512-mic3aOdiq01DuSVx0TseaEzMIVqebMZ0Z3vaeDhFEh9bsc24hV1TFvN74reA2vs08D0ZWfNjAcJ3UbVLaBss+g==", + "dev": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + } + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "concat": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/concat/-/concat-1.0.3.tgz", + "integrity": "sha1-QPM1MInWVGdpXLGIa0Xt1jfYzKg=", + "dev": true, + "requires": { + "commander": "^2.9.0" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "copyfiles": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/copyfiles/-/copyfiles-2.4.1.tgz", + "integrity": "sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg==", + "dev": true, + "requires": { + "glob": "^7.0.5", + "minimatch": "^3.0.3", + "mkdirp": "^1.0.4", + "noms": "0.0.0", + "through2": "^2.0.1", + "untildify": "^4.0.0", + "yargs": "^16.1.0" + } + }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", + "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-symbols": "^1.0.2", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.1", + "is-string": "^1.0.7", + "is-weakref": "^1.0.1", + "object-inspect": "^1.11.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, + "get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + } + }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "graceful-fs": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "dev": true + }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "requires": { + "has-bigints": "^1.0.1" + } + }, + "is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", + "dev": true + }, + "is-core-module": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", + "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-negative-zero": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", + "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", + "dev": true + }, + "is-number-object": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", + "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-shared-array-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", + "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==", + "dev": true + }, + "is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, + "is-weakref": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.1.tgz", + "integrity": "sha512-b2jKc2pQZjaeFYWEf7ScFj+Be1I+PXmlu572Q8coTXZ+LD/QQZ7ShPMst8h16riVgyXTQwUsFEl74mDvc/3MHQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.0" + } + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "noms": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/noms/-/noms-0.0.0.tgz", + "integrity": "sha1-2o69nzr51nYJGbJ9nNyAkqczKFk=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "~1.0.31" + } + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "npm-run-all": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", + "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "memorystream": "^0.3.1", + "minimatch": "^3.0.4", + "pidtree": "^0.3.0", + "read-pkg": "^3.0.0", + "shell-quote": "^1.6.1", + "string.prototype.padend": "^3.0.0" + } + }, + "object-inspect": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", + "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "pidtree": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", + "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", + "dev": true + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "select2": { + "version": "4.1.0-rc.0", + "resolved": "https://registry.npmjs.org/select2/-/select2-4.1.0-rc.0.tgz", + "integrity": "sha512-Hr9TdhyHCZUtwznEH2CBf7967mEM0idtJ5nMtjvk3Up5tPukOLXbHUNmh10oRfeNIhj+3GD3niu+g6sVK+gK0A==", + "dev": true + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "shell-quote": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz", + "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==", + "dev": true + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", + "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==", + "dev": true + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "string.prototype.padend": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.3.tgz", + "integrity": "sha512-jNIIeokznm8SD/TZISQsZKYu7RJyheFNt84DUPrh482GC8RVp2MKqm2O5oBRdGxbDQoXrhhWtPIWQOiy20svUg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + } + }, + "string.prototype.trimend": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "string.prototype.trimstart": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "uglify-js": { + "version": "3.14.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.3.tgz", + "integrity": "sha512-mic3aOdiq01DuSVx0TseaEzMIVqebMZ0Z3vaeDhFEh9bsc24hV1TFvN74reA2vs08D0ZWfNjAcJ3UbVLaBss+g==", + "dev": true + }, + "unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + } + }, + "untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 000000000..59f32b60c --- /dev/null +++ b/package.json @@ -0,0 +1,36 @@ +{ + "name": "django-autocomplete-light", + "version": "$short", + "description": "A fresh approach to autocomplete implementations, specially for Django. Status: v3 stable, 2.x.x stable, 1.x.x deprecated. Please DO regularely ping us with your link at #yourlabs IRC channel https://django-autocomplete-light.readthedocs.io/", + "directories": { + "doc": "docs" + }, + "scripts": { + "build": "npm-run-all copy-deps compile-js minify-js", + "compile-js": "node select2.build.js", + "minify-js": "npm-run-all minify-autocomplete minify-select2", + "minify-autocomplete": "uglifyjs src/dal/static/autocomplete_light/autocomplete_light.js -c -o src/dal/static/autocomplete_light/autocomplete_light.min.js -m reserved=['$','yl','django','jQuery','jquery','select2'] --source-map", + "minify-select2": "uglifyjs src/dal_select2/static/autocomplete_light/select2.js -c -o src/dal_select2/static/autocomplete_light/select2.min.js -m reserved=['$','yl','django','jQuery','jquery','select2'] --source-map", + "copy-deps": "copyfiles \"node_modules/select2/dist/css/*.css\" \"src/dal_select2/static/vendor/select2/dist\" -E -u 3" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/yourlabs/django-autocomplete-light.git" + }, + "author": "James Pic", + "license": "MIT", + "bugs": { + "url": "https://github.com/yourlabs/django-autocomplete-light/issues" + }, + "homepage": "https://django-autocomplete-light.readthedocs.io/", + "devDependencies": { + "concat": "^1.0.3", + "copyfiles": "^2.4.1", + "npm-run-all": "^4.1.5", + "select2": "^4.1.0-rc.0", + "uglify-js": "^3.14.3" + }, + "dependencies": { + "semver": "latest" + } +} diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 000000000..8111a5336 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,3 @@ +[pytest] +addopts = --nomigrations --create-db --reuse-db +DJANGO_SETTINGS_MODULE=settings.base diff --git a/release.sh b/release.sh new file mode 100755 index 000000000..3850a5a22 --- /dev/null +++ b/release.sh @@ -0,0 +1,45 @@ +#!/bin/bash -eux +# Release a new version of django-autocomplete-light +# +# Usage: ./release.sh 1.2.3-rc0 + +if [ -z "${1-}" ]; then + grep '^# ' $0 + exit 1 +fi + +if git tag -l | grep "^${1}\$"; then + echo Tag $1 already exists + exit 1 +fi + +stashed=0 +if [[ $(git diff --stat) != '' ]]; then + git stash + stashed=1 +fi + +npm install +npm run build +if [[ $(git diff --stat) != '' ]]; then + git add src/dal/static/autocomplete_light/i18n/ + git commit -am "Rebuild static" || echo No static to rebuild +fi + +sed -i "s/version=[^,]*,/version='$1',/" setup.py +sed -i "s/release = [^,]*,/release = '$1'/" docs/conf.py +short=$(echo $1 | grep -Eo '[^.]+\.[^.]+') +sed -i "s/version = [^,]*,/version = '$short'/" docs/conf.py + +source ~/.github +echo -e "$(python changelog.py $1)\n$(cat CHANGELOG)" > CHANGELOG +git add setup.py docs/conf.py CHANGELOG +git commit -m "Release $1" +git tag $1 +python setup.py sdist +twine upload dist/django-autocomplete-light-${1/-/}.tar.gz +git push origin master $1 + +if [[ $stashed -eq 1 ]]; then + git stash apply +fi diff --git a/select2.build.js b/select2.build.js new file mode 100644 index 000000000..eeae65034 --- /dev/null +++ b/select2.build.js @@ -0,0 +1,46 @@ +/* + * Pull all JS files into single file. + */ + +//requiring path and fs modules +const path = require('path'); +const fs = require('fs'); +const UglifyJS = require('uglify-js'); + +// Gather list of translation files +const directoryPath = path.join(__dirname, 'node_modules/select2/dist/js/i18n'); + +function getLanguageFiles(cb) { + fs.readdir(directoryPath, function (err, files) { + if (err) { + return console.log('Unable to scan directory: ' + err); + } + let fileArray = []; + files.forEach(function (file) { + if (file.endsWith('.js')) { + fileArray.push({file: file, path: path.join(directoryPath, file)}) + } + }); + cb(fileArray); + }); +} + +getLanguageFiles(function (files) { + files.forEach(function (file) { + let license, contents; + [license, ...contents] = fs.readFileSync(file.path, "utf8").split('\n'); + let code = `${license} + + var dalLoadLanguage = function (jQuery) { + ${contents.join('')} + } + var event = new CustomEvent("dal-language-loaded", { lang: "${file.file.slice(0, -3)}"}); + document.dispatchEvent(event);`; + let inputs = {}; + inputs[file.file] = code; + let result = UglifyJS.minify(inputs, {output: {comments: true}}) + fs.writeFile(`src/dal/static/autocomplete_light/i18n/${file.file}`, result.code, (err => { + if (err) throw err; + })) + }); +}); \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 000000000..06581181c --- /dev/null +++ b/setup.py @@ -0,0 +1,56 @@ +import os + +from setuptools import setup, find_packages + + +# Utility function to read the README file. +# Used for the long_description. It's nice because now 1) we have a top-level +# README file and 2) it's easier to type in the README file than to put a raw +# string in below ... +def read(fname): + return open(os.path.join(os.path.dirname(__file__), fname)).read() + +setup( + name='django-autocomplete-light', + version='3.9.8rc12', + description='Fresh autocompletes for Django', + author='James Pic', + author_email='jamespic@gmail.com', + url='http://django-autocomplete-light.rtfd.org', + project_urls={ + 'Source': 'https://github.com/yourlabs/django-autocomplete-light', + }, + packages=find_packages('src'), + package_dir={'': 'src'}, + include_package_data=True, + zip_safe=False, + long_description=read('README'), + license='MIT', + keywords='django autocomplete', + install_requires=[ + 'django>=3.2', + 'six', + ], + extras_require={ + 'nested': ['django-nested-admin>=3.0.21'], + 'tags': ['django-taggit'], + 'genericm2m': ['django-generic-m2m'], + 'gfk': ['django-querysetsequence>=0.11'], + }, + classifiers=[ + 'Development Status :: 5 - Production/Stable', + 'Environment :: Web Environment', + 'Framework :: Django', + 'Framework :: Django :: 3.2', + 'Framework :: Django :: 4.0', + 'Framework :: Django :: 4.1', + 'Framework :: Django :: 4.2', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: MIT License', + 'Operating System :: OS Independent', + 'Programming Language :: Python', + 'Programming Language :: Python :: 3', + 'Topic :: Internet :: WWW/HTTP', + 'Topic :: Software Development :: Libraries :: Python Modules', + ] +) diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 000000000..ff17ff242 --- /dev/null +++ b/src/__init__.py @@ -0,0 +1 @@ +"""Optional dependencies for DAL.""" diff --git a/src/dal/__init__.py b/src/dal/__init__.py new file mode 100644 index 000000000..5c4b6c317 --- /dev/null +++ b/src/dal/__init__.py @@ -0,0 +1,5 @@ +""" +Welcome to django-autocomplete-light. + +For examples, see the test_project/ directory of the repository. +""" diff --git a/src/dal/autocomplete.py b/src/dal/autocomplete.py new file mode 100644 index 000000000..7f201b893 --- /dev/null +++ b/src/dal/autocomplete.py @@ -0,0 +1,92 @@ +"""Optionnal shortcuts module for DAL. + +This module exports all the public classes for the project. It imports dal +module classes and extension module classes by checking the INSTALLED_APPS +setting: + +- if dal_select2 is present, import classes from dal_select2_*, +- with dal_queryset_sequence, import views and fields from it, +- with dal_queryset_sequence and dal_select2, import from + dal_select2_queryset_sequence, +- with django.contrib.contenttypes, import dal_contenttypes, +- with genericm2m, import dal_genericm2m, +- with gm2m, import dal_gm2m, +- with taggit, import dal_taggit, +- with tagulous, import dal_tagulous. + +Note that using this module is optional. +""" + +from django.conf import settings as django_settings + +from .forms import FutureModelForm +from .views import ViewMixin +from .widgets import ( + Select, + SelectMultiple, +) + + +def _installed(*apps): + for app in apps: + if app not in django_settings.INSTALLED_APPS: + return False + return True + + +if _installed('dal_select2'): + from dal_select2.widgets import ( + Select2, + Select2Multiple, + ModelSelect2, + ModelSelect2Multiple, + TagSelect2, + ListSelect2 + ) + from dal_select2.views import ( + Select2QuerySetView, + Select2GroupQuerySetView, + Select2ListView, + Select2GroupListView + ) + from dal_select2.fields import ( + Select2ListChoiceField, + Select2ListCreateChoiceField + ) + +if _installed('dal_queryset_sequence'): + from dal_queryset_sequence.fields import ( + QuerySetSequenceModelField, + QuerySetSequenceModelMultipleField, + GenericForeignKeyModelField, + ) + from dal_queryset_sequence.views import ( + BaseQuerySetSequenceView, + ) + from queryset_sequence import QuerySetSequence + +if _installed('dal_select2', 'dal_queryset_sequence'): + from dal_select2_queryset_sequence.views import ( + Select2QuerySetSequenceView, + ) + from dal_select2_queryset_sequence.widgets import ( + QuerySetSequenceSelect2, + QuerySetSequenceSelect2Multiple, + ) + from dal_select2_queryset_sequence.fields import ( + Select2GenericForeignKeyModelField, + ) + +if _installed('dal_select2') and _installed('taggit'): + from dal_select2_taggit.widgets import TaggitSelect2 + +if _installed('dal_select2') and _installed('tagging'): + from dal_select2_tagging.widgets import TaggingSelect2 + +if _installed('genericm2m') and _installed('dal_queryset_sequence'): + from dal_genericm2m_queryset_sequence.fields import ( + GenericM2MQuerySetSequenceField + ) + +if _installed('gm2m') and _installed('dal_queryset_sequence'): + from dal_gm2m_queryset_sequence.fields import GM2MQuerySetSequenceField diff --git a/src/dal/forms.py b/src/dal/forms.py new file mode 100644 index 000000000..1bc4baec5 --- /dev/null +++ b/src/dal/forms.py @@ -0,0 +1,178 @@ +""" +tl;dr: See FutureModelForm's docstring. + +Many apps provide new related managers to extend your django models with. For +example, django-tagulous provides a TagField which abstracts an M2M relation +with the Tag model, django-gm2m provides a GM2MField which abstracts an +relation, django-taggit provides a TaggableManager which abstracts a relation +too, django-generic-m2m provides RelatedObjectsDescriptor which abstracts a +relation again. + +While that works pretty well, it gets a bit complicated when it comes to +encapsulating the business logic for saving such data in a form object. This is +three-part problem: + +- getting initial data, +- saving instance attributes, +- saving relations like reverse relations or many to many. + +Django's ModelForm calls the model field's ``value_from_object()`` method to +get the initial data. ``FutureModelForm`` tries the ``value_from_object()`` +method from the form field instead, if defined. Unlike the model field, the +form field doesn't know its name, so ``FutureModelForm`` passes it when calling +the form field's ``value_from_object()`` method. + +Django's ModelForm calls the form field's ``save_form_data()`` in two +occasions: + +- in ``_post_clean()`` for model fields in ``Meta.fields``, +- in ``_save_m2m()`` for model fields in ``Meta.virtual_fields`` and + ``Meta.many_to_many``, which then operate on an instance which as a PK. + +If we just added ``save_form_data()`` to form fields like for +``value_from_object()`` then it would be called twice, once in +``_post_clean()`` and once in ``_save_m2m()``. Instead, ``FutureModelForm`` +would call the following methods from the form field, if defined: + +- ``save_object_data()`` in ``_post_clean()``, to set object attributes for a + given value, +- ``save_relation_data()`` in ``_save_m2m()``, to save relations for a given + value. + +For example: + +- a generic foreign key only sets instance attributes, its form field would do + that in ``save_object_data()``, +- a tag field saves relations, its form field would do that in + ``save_relation_data()``. +""" + +from itertools import chain + +from django import forms + + +class FutureModelForm(forms.ModelForm): + """ + ModelForm which adds extra API to form fields. + + Form fields may define new methods for FutureModelForm: + + - ``FormField.value_from_object(instance, name)`` should return the initial + value to use in the form, overrides ``ModelField.value_from_object()`` + which is what ModelForm uses by default, + - ``FormField.save_object_data(instance, name, value)`` should set instance + attributes. Called by ``save()`` **before** writing the database, when + ``instance.pk`` may not be set, it overrides + ``ModelField.save_form_data()`` which is normally used in this occasion + for non-m2m and non-virtual model fields. + - ``FormField.save_relation_data(instance, name, value)`` should save + relations required for value on the instance. Called by ``save()`` + **after** writing the database, when ``instance.pk`` is necessarily set, + it overrides ``ModelField.save_form_data()`` which is normally used in + this occasion for m2m and virtual model fields. + + For complete rationale, see this module's docstring. + """ + + def __init__(self, *args, **kwargs): + """Override that uses a form field's ``value_from_object()``.""" + super(FutureModelForm, self).__init__(*args, **kwargs) + + for name, field in self.fields.items(): + if not hasattr(field, 'value_from_object'): + continue + + self.initial[name] = field.value_from_object(self.instance, name) + + def _post_clean(self): + """Override that uses the form field's ``save_object_data()``.""" + super(FutureModelForm, self)._post_clean() + + for name, field in self.fields.items(): + if not hasattr(field, 'save_object_data'): + continue + + field.save_object_data( + self.instance, + name, + self.cleaned_data.get(name, None), + ) + + def _save_m2m(self): # noqa + """Override that uses the form field's ``save_object_data()``.""" + cleaned_data = self.cleaned_data + exclude = self._meta.exclude + fields = self._meta.fields + opts = self.instance._meta + + # Added to give the field a chance to do the work + handled = [] + for name, field in self.fields.items(): + if not hasattr(field, 'save_relation_data'): + continue + + field.save_relation_data( + self.instance, + name, + cleaned_data[name] + ) + + handled.append(name) + + # Note that for historical reasons we want to include also + # virtual_fields here. (GenericRelation was previously a fake + # m2m field). + virtual_fields = getattr(opts, 'virtual_fields', []) + if not virtual_fields: + virtual_fields = getattr(opts, 'private_fields', []) + for f in chain(opts.many_to_many, virtual_fields): + # Added to give the form field a chance to do the work + if f.name in handled: + continue + + if not hasattr(f, 'save_form_data'): + continue + if fields and f.name not in fields: + continue + if exclude and f.name in exclude: + continue + if f.name in cleaned_data: + f.save_form_data(self.instance, cleaned_data[f.name]) + + def save(self, commit=True): + """Backport from Django 1.9+ for 1.8.""" + if self.errors: + raise ValueError( + "The %s could not be %s because the data didn't validate." % ( + self.instance._meta.object_name, + 'created' if self.instance._state.adding else 'changed', + ) + ) + if commit: + # If committing, save the instance and the m2m data immediately. + self.instance.save() + self._save_m2m() + else: + # If not committing, add a method to the form to allow deferred + # saving of m2m data. + self.save_m2m = self._save_m2m + return self.instance + + @classmethod + def as_urls(cls): + """ + Create a list of url patterns, to be called in url.py. + + Example:: + + urlpattern.append(*ModelForm.as_url()) + + Iterate over the fields to call the as_url() method from the + GenericForeignKeyField + """ + return [ + value.as_url(cls) + for key, value in cls.__dict__['declared_fields'].items() + if hasattr(value.__class__, 'as_url') + ] # checks if its the right object diff --git a/src/dal/forward.py b/src/dal/forward.py new file mode 100644 index 000000000..c4eae44f7 --- /dev/null +++ b/src/dal/forward.py @@ -0,0 +1,169 @@ +"""Classes for class-based forward declaration.""" + + +class Forward(object): + """Base class for autocomplete forward declaration.""" + + @property + def type(self): + """Forward type. Should be implemented in subclasses.""" + raise NotImplementedError("Please use one of my subclasses") + + def to_dict(self): + """Convert to dictionary which will be rendered as JSON.""" + return { + "type": self.type + } + + +class Field(Forward): + """Forward field value. + + The type of the forwarded value from the field is either string, list of + strings or boolean. + + The following rules are used to deduce the forwarded type. + + - If there is only one field in the form or subform with name ``src`` + and this field is a checkbox without ``value`` HTML-attribute, + then boolean value indicating if this checkbox is checked is forwarded. + + - If there is only one field in the form or subform with name ``src`` + and it has ``multiple`` HTML-attribute, then this field is forwarded as a + list of strings, containing values from this field. + + - If there are one or more fields in the form with name ``src`` and all of + them are checkboxes with HTML-attribute ``value`` set the list of strings + containing checked checkboxes is forwarded. + + - Otherwise ``src`` field value forwarded as a string. + + .. py:attribute:: src + + The name of the form field whose value will be forwarded to a view. + + .. py:attribute:: dst + + The name of the key of the forwarded value from the src field in the + forwarded dictionary. If this value is ``None``, then the key is + ``src``. + """ + + type = "field" + + def __init__(self, src, dst=None): + """Instantiate a forwarded field value.""" + self.src = src + self.dst = dst + + def to_dict(self): + """Convert to dictionary which will be rendered as JSON.""" + d = super(Field, self).to_dict() + + d.update(src=self.src) + if self.dst is not None: + d.update(dst=self.dst) + + return d + + +class Const(Forward): + """Forward arbitrary constant value. + + .. py:attribute:: val + + The value to forward. Must be JSON-serializable. + + .. py:attribute:: dst + + The name of the key of the forwarded value. + """ + + type = "const" + + def __init__(self, val, dst): + """Instantiate a forwarded constant value.""" + self.val = val + self.dst = dst + + def to_dict(self): + """Convert to dictionary which will be rendered as JSON.""" + d = super(Const, self).to_dict() + + d.update(val=self.val) + d.update(dst=self.dst) + + return d + + +class JavaScript(Forward): + """Run registered javascript handler and forward its returned value. + + You can register custom forward handler in your JS code as follows: + + .. code-block:: javascript + + yl.registerForwardHandler("your_handler", function (autocompleteElement) { + // your code here + }); + + Then if your add ``JavaScript("your_handler", "some_value")`` to your + forwards declaration, your function will be called, autocomplete field + HTML element will be passed as ``autocompleteElement`` and returned value + will be added to forward dictionary with ``some_value`` key. + + .. py:attribute:: handler + + The name of the registered handler. + + .. py:attribute:: dst + + The name of the key of the forwarded value from the src field in the + forwarded dictionary. If this value is ``None``, then the key is + ``handler`` + """ + + type = "javascript" + + def __init__(self, handler, dst=None): + """Initialize Javascript class.""" + self.handler = handler + self.dst = dst + + def to_dict(self): + """Convert to dictionary which will be rendered as JSON.""" + d = super(JavaScript, self).to_dict() + + d.update(handler=self.handler) + d.update(dst=self.dst) + + return d + + +class Self(Forward): + """Forward own value. + + The same as :class:`Field`, except that `src` is always this field + itself. + + .. py:attribute:: dst + + The name of the key of the forwarded value from the src field in the + forwarded dictionary. If this value is ``None``, then the key is + ``self``. + """ + + type = "self" + + def __init__(self, dst=None): + """Instantiate a forwarded field value.""" + self.dst = dst + + def to_dict(self): + """Convert to dictionary which will be rendered as JSON.""" + d = super(Self, self).to_dict() + + if self.dst is not None: + d.update(dst=self.dst) + + return d diff --git a/src/dal/static/autocomplete_light/autocomplete_light.js b/src/dal/static/autocomplete_light/autocomplete_light.js new file mode 100644 index 000000000..6149f7187 --- /dev/null +++ b/src/dal/static/autocomplete_light/autocomplete_light.js @@ -0,0 +1,485 @@ +/*! + * Django Autocomplete Light + */ + +var yl = yl || {}; +yl.functions = yl.functions || {}; +/** + * Register your own JS function for DAL. + * + * @param name The name of your function. This should be the same as the widget + * `autocomplete_function` property value. + * @param func The callback that will initialize your custom autocomplete. + */ +yl.registerFunction = function (name, func) { + if (this.functions.hasOwnProperty(name)) { + // This function already exists to show an error and skip. + console.error('The DAL function "' + name + '" has already been registered.'); + return + } + if (typeof func != 'function') { + // It's not a function kill it. + throw new Error('The custom DAL function must be a function.'); + } + this.functions[name] = func; + var event = new CustomEvent('dal-function-registered.' + name, {detail: {name: name, func: func}}) + window.dispatchEvent(event); +}; + + +window.addEventListener("load", function () { + + // Check if `django.jQuery` exists otherwise set `django.jQuery` to non namespaced jQuery. + window.django = window.django || {}; + if (!django.hasOwnProperty('jQuery') && jQuery !== 'undefined') { + django.jQuery = jQuery; + } + + (function ($) { + $.fn.getFormPrefix = function () { + /* Get the form prefix for a field. + * + * For example: + * + * $(':input[name$=owner]').getFormsetPrefix() + * + * Would return an empty string for an input with name 'owner' but would return + * 'inline_model-0-' for an input named 'inline_model-0-owner'. + */ + var parts = $(this).attr('name').split('-'); + var prefix = ''; + + for (var i in parts) { + var testPrefix = parts.slice(0, -i).join('-'); + if (!testPrefix.length) continue; + testPrefix += '-'; + + var result = $(':input[name^=' + testPrefix + ']') + + if (result.length) { + return testPrefix; + } + } + + return ''; + } + + $.fn.getFormPrefixes = function () { + /* + * Get the form prefixes for a field, from the most specific to the least. + * + * For example: + * + * $(':input[name$=owner]').getFormPrefixes() + * + * Would return: + * - [''] for an input named 'owner'. + * - ['inline_model-0-', ''] for an input named 'inline_model-0-owner' (i.e. nested with a nested inline). + * - ['sections-0-items-0-', 'sections-0-', ''] for an input named 'sections-0-items-0-product' + * (i.e. nested multiple time with django-nested-admin). + */ + var parts = $(this).attr('name').split('-').slice(0, -1); + var prefixes = []; + + for (i = 0; i < parts.length; i += 2) { + var testPrefix = parts.slice(0, -i || parts.length).join('-'); + if (!testPrefix.length) + continue; + + testPrefix += '-'; + + var result = $(':input[name^=' + testPrefix + ']') + + if (result.length) + prefixes.push(testPrefix); + } + + prefixes.push(''); + + return prefixes; + } + + /* + * This ensures the Language file is loaded and passes it our jQuery. + */ + if (typeof dalLoadLanguage !== 'undefined') { + dalLoadLanguage($); + } else { + document.addEventListener('dal-language-loaded', function (e) { + // `e.lang` is the language that was loaded. + dalLoadLanguage($); + }) + } + + // Fire init event for yl.registerFunction() execution. + var event = new CustomEvent('dal-init-function'); + document.dispatchEvent(event); + + var initialized = []; + + $.fn.excludeTemplateForms = function() { + // exclude elements that contain '__prefix__' in their id + // these are used by django formsets for template forms + return this.not('[id*=__prefix__]').filter(function() { + // exclude elements that contain '-empty-' in their ids + // these are used by django-nested-admin for nested template formsets + // note that the filter also ensures that 'empty' is not actually the related_name for some relation + // by ensuring that it is not surrounded by numbers on both sides + return !this.id.match(/-empty-/) || this.id.match(/-\d+-empty-\d+-/); + }); + } + + /** + * Initialize a field element. This function calls the registered init function + * and ensures that the element is only initialized once. + * + * @param element The field to be initialized + */ + function initialize(element) { + if (typeof element === 'undefined' || typeof element === 'number') { + element = this; + } + + // Ensure element is not already initialized. + if (initialized.indexOf(element) >= 0) { + return; + } + + // The DAL function to execute. + var dalFunction = $(element).attr('data-autocomplete-light-function'); + + if (yl.functions.hasOwnProperty(dalFunction) && typeof yl.functions[dalFunction] == 'function') { + // If the function has been registered call it. + yl.functions[dalFunction]($, element); + } else if (yl.functions.hasOwnProperty(dalFunction)) { + // If the function exists but has not been registered wait for it to be registered. + window.addEventListener('dal-function-registered.' + dalFunction, function (e) { + yl.functions[dalFunction]($, element); + }) + } else { + // Otherwise notify that the function should be registered. + console.warn('Your custom DAL function "' + dalFunction + '" uses a deprecated event listener that will be removed in future versions. https://django-autocomplete-light.readthedocs.io/en/master/tutorial.html#overriding-javascript-code') + } + + // Fire init event for custom function execution. + // DEPRECATED + $(element).trigger('autocompleteLightInitialize'); + + // Add element to the array of already initialized fields + initialized.push(element); + + // creates and dispatches the event to notify of the initialization completed + var dalElementInitializedEvent = new CustomEvent("dal-element-initialized", { + detail: { + element: element, + } + }); + + document.dispatchEvent(dalElementInitializedEvent); + } + + if (!window.__dal__initialize) { + window.__dal__initialize = initialize; + + $(document).ready(function () { + $('[data-autocomplete-light-function]').excludeTemplateForms().each(initialize); + }); + + /** + * Helper function to determine if the element is being dragged, so that we + * don't initialize the autocomplete fields. They will get initialized when the dragging stops. + * + * @param element The element to check + * @returns {boolean} + */ + function isDraggingElement(element) { + return 'classList' in element && element.classList.contains('ui-sortable-helper'); + } + + if ('MutationObserver' in window) { + new MutationObserver(function (mutations) { + var mutationRecord; + var addedNode; + + for (var i = 0; i < mutations.length; i++) { + mutationRecord = mutations[i]; + + if (mutationRecord.addedNodes.length > 0) { + for (var j = 0; j < mutationRecord.addedNodes.length; j++) { + addedNode = mutationRecord.addedNodes[j]; + if (isDraggingElement(addedNode)) return; + $(addedNode).find('[data-autocomplete-light-function]').excludeTemplateForms().each(initialize); + } + } + } + + }).observe(document.documentElement, {childList: true, subtree: true}); + } else { + $(document).on('DOMNodeInserted', function (e) { + if (isDraggingElement(e.target)) return; + $(e.target).find('[data-autocomplete-light-function]').excludeTemplateForms().each(initialize); + }); + } + } + + // using jQuery + function getCookie(name) { + var cookieValue = null; + if (document.cookie && document.cookie != '') { + var cookies = document.cookie.split(';'); + for (var i = 0; i < cookies.length; i++) { + var cookie = $.trim(cookies[i]); + // Does this cookie string begin with the name we want? + if (cookie.substring(0, name.length + 1) == (name + '=')) { + cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); + break; + } + } + } + return cookieValue; + } + + document.csrftoken = getCookie('csrftoken'); + if (document.csrftoken === null) { + // Try to get CSRF token from DOM when cookie is missing + var $csrf = $('form :input[name="csrfmiddlewaretoken"]'); + if ($csrf.length > 0) { + document.csrftoken = $csrf[0].value; + } + } + })(django.jQuery); + + // Does the same thing as django's admin/js/autocomplete.js, but uses yl.jQuery. + (function ($) { + 'use strict'; + var init = function ($element, options) { + var settings = $.extend({ + ajax: { + data: function (params) { + return { + term: params.term, + page: params.page, + app_label: $element.data('app-label'), + model_name: $element.data('model-name'), + field_name: $element.data('field-name') + }; + } + } + }, options); + $element.select2(settings); + }; + + $.fn.djangoAdminSelect2 = function (options) { + var settings = $.extend({}, options); + $.each(this, function (i, element) { + var $element = $(element); + init($element, settings); + }); + return this; + }; + + $(function () { + // Initialize all autocomplete widgets except the one in the template + // form used when a new formset is added. + $('.admin-autocomplete').not('[name*=__prefix__]').djangoAdminSelect2(); + }); + + $(document).on('formset:added', (function () { + return function (event, $newFormset) { + return $newFormset.find('.admin-autocomplete').djangoAdminSelect2(); + }; + })(this)); + }(django.jQuery)); + + (function ($, yl) { + yl.forwardHandlerRegistry = yl.forwardHandlerRegistry || {}; + + yl.registerForwardHandler = function (name, handler) { + yl.forwardHandlerRegistry[name] = handler; + }; + + yl.getForwardHandler = function (name) { + return yl.forwardHandlerRegistry[name]; + }; + + function getForwardStrategy(element) { + var checkForCheckboxes = function () { + var all = true; + $.each(element, function (ix, e) { + if ($(e).attr("type") !== "checkbox") { + all = false; + } + }); + return all; + }; + + if (element.length === 1 && + element.attr("type") === "checkbox" && + element.attr("value") === undefined) { + // Single checkbox without 'value' attribute + // Boolean field + return "exists"; + } else if (element.length === 1 && + element.attr("multiple") !== undefined) { + // Multiple by HTML semantics. E. g. multiple select + // Multiple choice field + return "multiple"; + } else if (checkForCheckboxes()) { + // Multiple checkboxes or one checkbox with 'value' attribute. + // Multiple choice field represented by checkboxes + return "multiple"; + } else { + // Other cases + return "single"; + } + } + + /** + * Get fields with name `name` relative to `element` with considering form + * prefixes. + * @param element the element + * @param name name of the field + * @returns jQuery object with found fields or empty jQuery object if no + * field was found + */ + yl.getFieldRelativeTo = function (element, name) { + var prefixes = $(element).getFormPrefixes(); + + for (var i = 0; i < prefixes.length; i++) { + var fieldSelector = "[name=" + prefixes[i] + name + "]"; + var field = $(fieldSelector); + + if (field.length) { + return field; + } + } + + return $(); + }; + + /** + * Get field value which is put to forwarded dictionary + * @param field the field + * @returns forwarded value + */ + yl.getValueFromField = function (field) { + var strategy = getForwardStrategy(field); + var serializedField = $(field).serializeArray(); + + if ((serializedField == false) && ($(field).prop('disabled'))) { + $(field).prop('disabled', false); + serializedField = $(field).serializeArray(); + $(field).prop('disabled', true); + } + + var getSerializedFieldElementAt = function (index) { + // Return serializedField[index] + // or null if something went wrong + if (serializedField.length > index) { + return serializedField[index]; + } else { + return null; + } + }; + + var getValueOf = function (elem) { + // Return elem.value + // or null if something went wrong + if (elem.hasOwnProperty("value") && + elem.value !== undefined + ) { + return elem.value; + } else { + return null; + } + }; + + var getSerializedFieldValueAt = function (index) { + // Return serializedField[index].value + // or null if something went wrong + var elem = getSerializedFieldElementAt(index); + if (elem !== null) { + return getValueOf(elem); + } else { + return null; + } + }; + + if (strategy === "multiple") { + return serializedField.map( + function (item) { + return getValueOf(item); + } + ); + } else if (strategy === "exists") { + return serializedField.length > 0; + } else { + return getSerializedFieldValueAt(0); + } + }; + + yl.getForwards = function (element) { + var forwardElem, + forwardList, + forwardedData, + divSelector, + form; + divSelector = "div.dal-forward-conf#dal-forward-conf-for-" + + element.attr("id") + ", " + + "div.dal-forward-conf#dal-forward-conf-for_" + + element.attr("id"); + form = element.length > 0 ? $(element[0].form) : $(); + + forwardElem = + form.find(divSelector).find('script'); + if (forwardElem.length === 0) { + return; + } + try { + forwardList = JSON.parse(forwardElem.text()); + } catch (e) { + return; + } + + if (!Array.isArray(forwardList)) { + return; + } + + forwardedData = {}; + + $.each(forwardList, function (ix, field) { + var srcName, dstName; + if (field.type === "const") { + forwardedData[field.dst] = field.val; + } else if (field.type === "self") { + if (field.hasOwnProperty("dst")) { + dstName = field.dst; + } else { + dstName = "self"; + } + forwardedData[dstName] = yl.getValueFromField(element); + } else if (field.type === "field") { + srcName = field.src; + if (field.hasOwnProperty("dst")) { + dstName = field.dst; + } else { + dstName = srcName; + } + var forwardedField = yl.getFieldRelativeTo(element, srcName); + + if (!forwardedField.length) { + return; + } + + forwardedData[dstName] = yl.getValueFromField(forwardedField); + } else if (field.type === "javascript") { + var handler = yl.getForwardHandler(field.handler); + forwardedData[field.dst || field.handler] = handler(element); + } + + }); + return JSON.stringify(forwardedData); + }; + + })(django.jQuery, yl); +}); diff --git a/src/dal/static/autocomplete_light/autocomplete_light.min.js b/src/dal/static/autocomplete_light/autocomplete_light.min.js new file mode 100644 index 000000000..5d9c3ce27 --- /dev/null +++ b/src/dal/static/autocomplete_light/autocomplete_light.min.js @@ -0,0 +1 @@ +var yl=yl||{};yl.functions=yl.functions||{},yl.registerFunction=function(e,t){if(this.functions.hasOwnProperty(e))console.error('The DAL function "'+e+'" has already been registered.');else{if("function"!=typeof t)throw new Error("The custom DAL function must be a function.");this.functions[e]=t;t=new CustomEvent("dal-function-registered."+e,{detail:{name:e,func:t}});window.dispatchEvent(t)}},window.addEventListener("load",function(){window.django=window.django||{},django.hasOwnProperty("jQuery")||"undefined"===jQuery||(django.jQuery=jQuery),function($){$.fn.getFormPrefix=function(){var e,t=$(this).attr("name").split("-");for(e in t){var n=t.slice(0,-e).join("-");if(n.length)if($(":input[name^="+(n+="-")+"]").length)return n}return""},$.fn.getFormPrefixes=function(){var e=$(this).attr("name").split("-").slice(0,-1),t=[];for(i=0;ie?n[e]:null;return null!==e?r(e):null};return"multiple"===t?n.map(r):"exists"===t?0= (4, 0): + from django.contrib.admin.utils import lookup_spawns_duplicates +else: + from django.contrib.admin.utils import lookup_needs_distinct \ + as lookup_spawns_duplicates +from django.contrib.auth import get_permission_codename +from django.core.exceptions import ImproperlyConfigured, ValidationError +from django.db.models import Q +from django.http import HttpResponseBadRequest, HttpResponseNotAllowed +from django.template.loader import render_to_string +from django.views.generic.list import BaseListView + +import six + + +class ViewMixin(object): + """Common methods for autocomplete views. + + It is assumed this view will be used in conjunction with a Django + :py:class:`View` based class that will implement OPTIONS. + + .. py:attribute:: forwarded + + Dict of field values that were forwarded from the form, may be used to + filter autocompletion results based on the form state. See + ``linked_data`` example for reference. + + .. py:attribute:: q + + Query string as typed by the user in the autocomplete field. + """ + + http_method_allowed = ('GET', 'POST') + + def dispatch(self, request, *args, **kwargs): + """Set :py:attr:`forwarded` and :py:attr:`q`.""" + if request.method.upper() not in self.http_method_allowed: + return HttpResponseNotAllowed(self.http_method_allowed) + + try: + self.forwarded = json.loads( + getattr(request, request.method).get('forward', '{}') + ) + except ValueError: + return HttpResponseBadRequest('Invalid JSON data') + + if not isinstance(self.forwarded, dict): + return HttpResponseBadRequest('Not a JSON object') + + self.q = request.GET.get('q', '') + return super(ViewMixin, self).dispatch(request, *args, **kwargs) + + +class BaseQuerySetView(ViewMixin, BaseListView): + """Base view to get results from a QuerySet. + + .. py:attribute:: create_field + + Name of the field to use to create missing values. For example, if + create_field='title', and the user types in "foo", then the + autocomplete view will propose an option 'Create "foo"' if it can't + find any value matching "foo". When the user does click 'Create "foo"', + the autocomplete script should POST to this view to create the object + and get back the newly created object id. + + .. py:attribute:: model_field_name + + Name of the Model field to run filter against. + """ + + paginate_by = 10 + context_object_name = 'results' + model_field_name = 'name' + create_field = None + search_fields = [] + split_words = None + template = None + validate_create = None + + def has_more(self, context): + """For widgets that have infinite-scroll feature.""" + return context['page_obj'].has_next() if context['page_obj'] else False + + def get_result_value(self, result): + """Return the value of a result.""" + return str(result.pk) + + def get_result_label(self, result): + """Return the label of a result.""" + if self.template: + return render_to_string(self.template, {"result": result}) + else: + return six.text_type(result) + + def get_selected_result_label(self, result): + """Return the label of a selected result.""" + return self.get_result_label(result) + + def get_queryset(self): + """Filter the queryset with GET['q'].""" + qs = super(BaseQuerySetView, self).get_queryset() + + qs = self.get_search_results(qs, self.q) + + return qs + + def get_search_fields(self): + """Get the fields to search over.""" + if self.search_fields: + return self.search_fields + else: + return [self.model_field_name] + + def _construct_search(self, field_name): + """Apply keyword searches.""" + if field_name.startswith("^"): + return "%s__istartswith" % field_name[1:] + elif field_name.startswith("="): + return "%s__iexact" % field_name[1:] + elif field_name.startswith("@"): + return "%s__search" % field_name[1:] + else: + return "%s__icontains" % field_name + + def get_search_results(self, queryset, search_term): + """Filter the results based on the query.""" + search_fields = self.get_search_fields() + if search_fields and search_term: + orm_lookups = [ + self._construct_search(search_field) for search_field in search_fields + ] + if self.split_words is not None: + word_conditions = [] + for word in search_term.split(): + or_queries = [Q(**{orm_lookup: word}) for orm_lookup in orm_lookups] + word_conditions.append(reduce(operator.or_, or_queries)) + op_ = operator.or_ if self.split_words == "or" else operator.and_ + queryset = queryset.filter(reduce(op_, word_conditions)) + else: + or_queries = [ + Q(**{orm_lookup: search_term}) for orm_lookup in orm_lookups + ] + queryset = queryset.filter(reduce(operator.or_, or_queries)) + + if self.lookup_needs_distinct(queryset, orm_lookups): + queryset = queryset.distinct() + + return queryset + + def lookup_needs_distinct(self, queryset, orm_lookups): + """Return True if an orm_lookup requires calling qs.distinct().""" + return any( + lookup_spawns_duplicates(queryset.model._meta, search_spec) + for search_spec in orm_lookups + ) + + def create_object(self, text): + """Create an object given a text.""" + return self.get_queryset().get_or_create( + **{self.create_field: text})[0] + + def has_add_permission(self, request): + """Return True if the user has the permission to add a model.""" + if django.VERSION < (2, 0, 0): + auth = request.user.is_authenticated() + else: + auth = request.user.is_authenticated + + if not auth: + return False + + opts = self.get_queryset().model._meta + codename = get_permission_codename('add', opts) + return request.user.has_perm("%s.%s" % (opts.app_label, codename)) + + def post(self, request, *args, **kwargs): + """ + Create an object given a text after checking permissions. + + Runs self.validate() if self.validate_create is True. + """ + if not self.has_add_permission(request): + return http.HttpResponseForbidden() + + if not self.create_field: + raise ImproperlyConfigured('Missing "create_field"') + + text = request.POST.get('text', None) + + if text is None: + return http.HttpResponseBadRequest() + + if self.validate_create: + try: + self.validate(text) + except ValidationError as error: + if self.create_field in error.message_dict: + return http.JsonResponse(dict(error=error)) + + result = self.create_object(text) + + return http.JsonResponse({ + 'id': self.get_result_value(result), + 'text': self.get_selected_result_label(result), + }) + + def validate(self, text): + """ + Validate a given text for new option creation. + + Raise ValidationError or return None. + """ + model = self.get_queryset().model + obj = model(**{self.create_field: text}) + obj.full_clean() diff --git a/src/dal/widgets.py b/src/dal/widgets.py new file mode 100644 index 000000000..1b6e22675 --- /dev/null +++ b/src/dal/widgets.py @@ -0,0 +1,193 @@ +"""Autocomplete widgets bases.""" + +import copy +import json + +from dal import forward + +from django import VERSION +from django import forms +try: + from django.urls import reverse +except ImportError: + from django.core.urlresolvers import reverse +from django.utils.safestring import mark_safe + +import six + + +class WidgetMixin(object): + """Base mixin for autocomplete widgets. + + .. py:attribute:: url + + Absolute URL to the autocomplete view for the widget. It can be set to + a URL name, in which case it will be reversed when the attribute is + accessed. + + .. py:attribute:: forward + + List of field names to forward to the autocomplete view, useful to + filter results using values of other fields in the form. + + Items of the list must be one of the following: + - string (e. g. "some_field"): forward a value from + the field with named "some_field"; + - ``dal.forward.Field("some_field")``: the same as above; + - ``dal.forward.Field("some_field", "dst_field")``: forward a value from + the field with named "some_field" as "dst_field"; + - ``dal.forward.Const("some_value", "dst_field")``: forward a constant + value "some_value" as "dst_field". + + .. py:attribute:: autocomplete_function + + Identifier of the javascript callback that should be + executed when such a widget is loaded in the DOM, + either on page load or dynamically. + """ + + def __init__(self, url=None, forward=None, *args, **kwargs): + """Instanciate a widget with a URL and a list of fields to forward.""" + self.url = url + self.forward = forward or [] + self.placeholder = kwargs.get("attrs", {}).get("data-placeholder") + super(WidgetMixin, self).__init__(*args, **kwargs) + + def build_attrs(self, *args, **kwargs): + """Build HTML attributes for the widget.""" + attrs = super(WidgetMixin, self).build_attrs(*args, **kwargs) + + if self.url is not None: + attrs['data-autocomplete-light-url'] = self.url + + autocomplete_function = getattr(self, 'autocomplete_function', None) + if autocomplete_function: + attrs.setdefault('data-autocomplete-light-function', + autocomplete_function) + return attrs + + def filter_choices_to_render(self, selected_choices): + """Replace self.choices with selected_choices.""" + self.choices = [c for c in self.choices if + six.text_type(c[0]) in selected_choices] + + @staticmethod + def _make_forward_dict(f): + """Convert forward declaration to a dictionary. + + A returned dictionary will be dumped to JSON while rendering widget. + """ + if isinstance(f, six.string_types): + return forward.Field(f).to_dict() + elif isinstance(f, forward.Forward): + return f.to_dict() + else: + raise TypeError("Cannot use {} as forwarded value".format(f)) + + def render_forward_conf(self, id): + """Render forward configuration for the field.""" + if self.forward: + return \ + '' + else: + return "" + + def render_options(self, *args): + """ + Django-compatibility method for option rendering. + + Should only render selected options, by setting self.choices before + calling the parent method. + + Remove this code when dropping support for Django<1.10. + """ + selected_choices_arg = 1 if VERSION < (1, 10) else 0 + + # Filter out None values, not needed for autocomplete + selected_choices = [six.text_type(c) for c + in args[selected_choices_arg] if c] + + all_choices = copy.copy(self.choices) + if self.url: + self.filter_choices_to_render(selected_choices) + elif not self.allow_multiple_selected: + if self.placeholder: + self.choices.insert(0, (None, "")) + + html = super(WidgetMixin, self).render_options(*args) + + self.choices = all_choices + + return html + + def optgroups(self, name, value, attrs=None): + """ + Exclude unselected self.choices before calling the parent method. + + Used by Django>=1.10. + """ + # Filter out None values, not needed for autocomplete + selected_choices = [six.text_type(c) for c in value if c] + all_choices = copy.copy(self.choices) + if self.url: + self.filter_choices_to_render(selected_choices) + elif not self.allow_multiple_selected: + if self.placeholder: + self.choices.insert(0, (None, "")) + result = super(WidgetMixin, self).optgroups(name, value, attrs) + self.choices = all_choices + return result + + def render(self, name, value, attrs=None, renderer=None, **kwargs): + """Call Django render together with `render_forward_conf`.""" + widget = super(WidgetMixin, self).render(name, value, attrs, **kwargs) + try: + field_id = attrs['id'] + except (KeyError, TypeError): + field_id = name + conf = self.render_forward_conf(field_id) + return mark_safe(widget + conf) + + def _get_url(self): + if self._url is None: + return None + + if '/' in self._url: + return self._url + + return reverse(self._url) + + def _set_url(self, url): + self._url = url + + url = property(_get_url, _set_url) + + +class Select(WidgetMixin, forms.Select): + """Replacement for Django's Select to render only selected choices.""" + + +class SelectMultiple(WidgetMixin, forms.SelectMultiple): + """Replacement SelectMultiple to render only selected choices.""" + + +class QuerySetSelectMixin(WidgetMixin): + """QuerySet support for choices.""" + + def filter_choices_to_render(self, selected_choices): + """Filter out un-selected choices if choices is a QuerySet.""" + try: + self.choices.queryset = self.choices.queryset.filter( + pk__in=[c for c in selected_choices if c] + ) + except ValueError: + # if selected_choices are invalid, do nothing + pass diff --git a/src/dal_contenttypes/__init__.py b/src/dal_contenttypes/__init__.py new file mode 100644 index 000000000..f8b96df94 --- /dev/null +++ b/src/dal_contenttypes/__init__.py @@ -0,0 +1 @@ +"""django.contrib.contenttypes support utilities.""" diff --git a/src/dal_contenttypes/fields.py b/src/dal_contenttypes/fields.py new file mode 100644 index 000000000..d3344a0ff --- /dev/null +++ b/src/dal_contenttypes/fields.py @@ -0,0 +1,63 @@ +"""Model choice fields that take a ContentType too: for generic relations.""" + +from django.contrib.contenttypes.models import ContentType + +import six + + +class ContentTypeModelFieldMixin(object): + """ + Common methods for form fields for GenericForeignKey. + + ModelChoiceFieldMixin expects options to look like:: + + + + With a ContentType of id 3 for that model, it becomes:: + + + """ + + def prepare_value(self, value): + """Return a ctypeid-objpk string for value.""" + if not value: + return '' + + if isinstance(value, six.string_types): + # Apparently Django's ModelChoiceField also expects two kinds of + # "value" to be passed in this method. + return value + + return '%s-%s' % (ContentType.objects.get_for_model(value).pk, + value.pk) + + +class ContentTypeModelMultipleFieldMixin(ContentTypeModelFieldMixin): + """Same as ContentTypeModelFieldMixin, but supports value list.""" + + def prepare_value(self, value): + """Run the parent's method for each value.""" + if not value: # ModelMultipleChoiceField does it too + return [] + + return [ + super(ContentTypeModelMultipleFieldMixin, self).prepare_value(v) + for v in value + ] + + +class GenericModelMixin(ContentTypeModelFieldMixin): + """GenericForeignKey support for form fields, with FutureModelForm. + + GenericForeignKey enforce editable=false, this class implements + save_object_data() and value_from_object() to allow FutureModelForm to + compensate. + """ + + def save_object_data(self, instance, name, value): + """Set the attribute, for FutureModelForm.""" + setattr(instance, name, value) + + def value_from_object(self, instance, name): + """Get the attribute, for FutureModelForm.""" + return getattr(instance, name) diff --git a/src/dal_genericm2m/__init__.py b/src/dal_genericm2m/__init__.py new file mode 100644 index 000000000..86d4acd26 --- /dev/null +++ b/src/dal_genericm2m/__init__.py @@ -0,0 +1 @@ +"""django-generic-m2m support for DAL.""" diff --git a/src/dal_genericm2m/fields.py b/src/dal_genericm2m/fields.py new file mode 100644 index 000000000..dc3796119 --- /dev/null +++ b/src/dal_genericm2m/fields.py @@ -0,0 +1,20 @@ +"""django-generic-m2m field mixin for FutureModelForm.""" + + +class GenericM2MFieldMixin(object): + """Form field mixin able to get / set instance generic-m2m relations.""" + + def value_from_object(self, instance, name): + """Return the list of related objects.""" + return [x.object for x in getattr(instance, name).all()] + + def save_relation_data(self, instance, name, value): + """Update the relation to be ``value``.""" + instance_field = getattr(instance, name) + + for related in instance_field.all(): + if related.object not in value: + instance_field.remove(related) + + for related in value: + instance_field.connect(related) diff --git a/src/dal_genericm2m_queryset_sequence/__init__.py b/src/dal_genericm2m_queryset_sequence/__init__.py new file mode 100644 index 000000000..9178ec012 --- /dev/null +++ b/src/dal_genericm2m_queryset_sequence/__init__.py @@ -0,0 +1 @@ +"""QuerySetSequence choices for django-generic-m2m relations.""" diff --git a/src/dal_genericm2m_queryset_sequence/fields.py b/src/dal_genericm2m_queryset_sequence/fields.py new file mode 100644 index 000000000..76f4dd139 --- /dev/null +++ b/src/dal_genericm2m_queryset_sequence/fields.py @@ -0,0 +1,10 @@ +"""Autocomplete fields for django-queryset-sequence and django-generic-m2m.""" + +from dal_genericm2m.fields import GenericM2MFieldMixin + +from dal_queryset_sequence.fields import QuerySetSequenceModelMultipleField + + +class GenericM2MQuerySetSequenceField(GenericM2MFieldMixin, + QuerySetSequenceModelMultipleField): + """Autocomplete field for GM2MField() for QuerySetSequence choices.""" diff --git a/src/dal_gm2m/__init__.py b/src/dal_gm2m/__init__.py new file mode 100644 index 000000000..98a4c82da --- /dev/null +++ b/src/dal_gm2m/__init__.py @@ -0,0 +1 @@ +"""django-gm2m support for DAL.""" diff --git a/src/dal_gm2m/fields.py b/src/dal_gm2m/fields.py new file mode 100644 index 000000000..407302380 --- /dev/null +++ b/src/dal_gm2m/fields.py @@ -0,0 +1,16 @@ +"""GM2MField support for autocomplete fields.""" + + +class GM2MFieldMixin(object): + """GM2MField ror FutureModelForm.""" + + def value_from_object(self, instance, name): + """Return the list of objects in the GM2MField relation.""" + return None if not instance.pk else [ + getattr(x, 'gm2m_tgt', x) + for x in getattr(instance, name).all() + ] + + def save_relation_data(self, instance, name, value): + """Save the relation into the GM2MField.""" + setattr(instance, name, value) diff --git a/src/dal_gm2m_queryset_sequence/__init__.py b/src/dal_gm2m_queryset_sequence/__init__.py new file mode 100644 index 000000000..436ee277d --- /dev/null +++ b/src/dal_gm2m_queryset_sequence/__init__.py @@ -0,0 +1 @@ +"""Couple django-generic-m2m and django-queryset-sequence for DAL.""" diff --git a/src/dal_gm2m_queryset_sequence/fields.py b/src/dal_gm2m_queryset_sequence/fields.py new file mode 100644 index 000000000..150c847ec --- /dev/null +++ b/src/dal_gm2m_queryset_sequence/fields.py @@ -0,0 +1,10 @@ +"""Form fields for using django-gm2m with QuerySetSequence.""" + +from dal_gm2m.fields import GM2MFieldMixin + +from dal_queryset_sequence.fields import QuerySetSequenceModelMultipleField + + +class GM2MQuerySetSequenceField(GM2MFieldMixin, + QuerySetSequenceModelMultipleField): + """Form field for QuerySetSequence to django-generic-m2m relation.""" diff --git a/src/dal_legacy_static/__init__.py b/src/dal_legacy_static/__init__.py new file mode 100644 index 000000000..bf97dd057 --- /dev/null +++ b/src/dal_legacy_static/__init__.py @@ -0,0 +1 @@ +"""Static files for Django < 2.0.""" diff --git a/src/dal_legacy_static/static/admin/css/autocomplete.css b/src/dal_legacy_static/static/admin/css/autocomplete.css new file mode 100644 index 000000000..3ef95d15f --- /dev/null +++ b/src/dal_legacy_static/static/admin/css/autocomplete.css @@ -0,0 +1,260 @@ +select.admin-autocomplete { + width: 20em; +} + +.select2-container--admin-autocomplete.select2-container { + min-height: 30px; +} + +.select2-container--admin-autocomplete .select2-selection--single, +.select2-container--admin-autocomplete .select2-selection--multiple { + min-height: 30px; + padding: 0; +} + +.select2-container--admin-autocomplete.select2-container--focus .select2-selection, +.select2-container--admin-autocomplete.select2-container--open .select2-selection { + border-color: #999; + min-height: 30px; +} + +.select2-container--admin-autocomplete.select2-container--focus .select2-selection.select2-selection--single, +.select2-container--admin-autocomplete.select2-container--open .select2-selection.select2-selection--single { + padding: 0; +} + +.select2-container--admin-autocomplete.select2-container--focus .select2-selection.select2-selection--multiple, +.select2-container--admin-autocomplete.select2-container--open .select2-selection.select2-selection--multiple { + padding: 0; +} + +.select2-container--admin-autocomplete .select2-selection--single { + background-color: #fff; + border: 1px solid #ccc; + border-radius: 4px; +} + +.select2-container--admin-autocomplete .select2-selection--single .select2-selection__rendered { + color: #444; + line-height: 30px; +} + +.select2-container--admin-autocomplete .select2-selection--single .select2-selection__clear { + cursor: pointer; + float: right; + font-weight: bold; +} + +.select2-container--admin-autocomplete .select2-selection--single .select2-selection__placeholder { + color: #999; +} + +.select2-container--admin-autocomplete .select2-selection--single .select2-selection__arrow { + height: 26px; + position: absolute; + top: 1px; + right: 1px; + width: 20px; +} + +.select2-container--admin-autocomplete .select2-selection--single .select2-selection__arrow b { + border-color: #888 transparent transparent transparent; + border-style: solid; + border-width: 5px 4px 0 4px; + height: 0; + left: 50%; + margin-left: -4px; + margin-top: -2px; + position: absolute; + top: 50%; + width: 0; +} + +.select2-container--admin-autocomplete[dir="rtl"] .select2-selection--single .select2-selection__clear { + float: left; +} + +.select2-container--admin-autocomplete[dir="rtl"] .select2-selection--single .select2-selection__arrow { + left: 1px; + right: auto; +} + +.select2-container--admin-autocomplete.select2-container--disabled .select2-selection--single { + background-color: #eee; + cursor: default; +} + +.select2-container--admin-autocomplete.select2-container--disabled .select2-selection--single .select2-selection__clear { + display: none; +} + +.select2-container--admin-autocomplete.select2-container--open .select2-selection--single .select2-selection__arrow b { + border-color: transparent transparent #888 transparent; + border-width: 0 4px 5px 4px; +} + +.select2-container--admin-autocomplete .select2-selection--multiple { + background-color: white; + border: 1px solid #ccc; + border-radius: 4px; + cursor: text; +} + +.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__rendered { + box-sizing: border-box; + list-style: none; + margin: 0; + padding: 0 5px; + width: 100%; +} + +.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__rendered li { + list-style: none; +} + +.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__placeholder { + color: #999; + margin-top: 5px; + float: left; +} + +.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__clear { + cursor: pointer; + float: right; + font-weight: bold; + margin: 5px; +} + +.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__choice { + background-color: #e4e4e4; + border: 1px solid #ccc; + border-radius: 4px; + cursor: default; + float: left; + margin-right: 5px; + margin-top: 5px; + padding: 0 5px; +} + +.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__choice__remove { + color: #999; + cursor: pointer; + display: inline-block; + font-weight: bold; + margin-right: 2px; +} + +.select2-container--admin-autocomplete .select2-selection--multiple .select2-selection__choice__remove:hover { + color: #333; +} + +.select2-container--admin-autocomplete[dir="rtl"] .select2-selection--multiple .select2-selection__choice, .select2-container--admin-autocomplete[dir="rtl"] .select2-selection--multiple .select2-selection__placeholder, .select2-container--admin-autocomplete[dir="rtl"] .select2-selection--multiple .select2-search--inline { + float: right; +} + +.select2-container--admin-autocomplete[dir="rtl"] .select2-selection--multiple .select2-selection__choice { + margin-left: 5px; + margin-right: auto; +} + +.select2-container--admin-autocomplete[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove { + margin-left: 2px; + margin-right: auto; +} + +.select2-container--admin-autocomplete.select2-container--focus .select2-selection--multiple { + border: solid #999 1px; + outline: 0; +} + +.select2-container--admin-autocomplete.select2-container--disabled .select2-selection--multiple { + background-color: #eee; + cursor: default; +} + +.select2-container--admin-autocomplete.select2-container--disabled .select2-selection__choice__remove { + display: none; +} + +.select2-container--admin-autocomplete.select2-container--open.select2-container--above .select2-selection--single, .select2-container--admin-autocomplete.select2-container--open.select2-container--above .select2-selection--multiple { + border-top-left-radius: 0; + border-top-right-radius: 0; +} + +.select2-container--admin-autocomplete.select2-container--open.select2-container--below .select2-selection--single, .select2-container--admin-autocomplete.select2-container--open.select2-container--below .select2-selection--multiple { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; +} + +.select2-container--admin-autocomplete .select2-search--dropdown .select2-search__field { + border: 1px solid #ccc; +} + +.select2-container--admin-autocomplete .select2-search--inline .select2-search__field { + background: transparent; + border: none; + outline: 0; + box-shadow: none; + -webkit-appearance: textfield; +} + +.select2-container--admin-autocomplete .select2-results > .select2-results__options { + max-height: 200px; + overflow-y: auto; +} + +.select2-container--admin-autocomplete .select2-results__option[role=group] { + padding: 0; +} + +.select2-container--admin-autocomplete .select2-results__option[aria-disabled=true] { + color: #999; +} + +.select2-container--admin-autocomplete .select2-results__option[aria-selected=true] { + background-color: #ddd; +} + +.select2-container--admin-autocomplete .select2-results__option .select2-results__option { + padding-left: 1em; +} + +.select2-container--admin-autocomplete .select2-results__option .select2-results__option .select2-results__group { + padding-left: 0; +} + +.select2-container--admin-autocomplete .select2-results__option .select2-results__option .select2-results__option { + margin-left: -1em; + padding-left: 2em; +} + +.select2-container--admin-autocomplete .select2-results__option .select2-results__option .select2-results__option .select2-results__option { + margin-left: -2em; + padding-left: 3em; +} + +.select2-container--admin-autocomplete .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option { + margin-left: -3em; + padding-left: 4em; +} + +.select2-container--admin-autocomplete .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option { + margin-left: -4em; + padding-left: 5em; +} + +.select2-container--admin-autocomplete .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option { + margin-left: -5em; + padding-left: 6em; +} + +.select2-container--admin-autocomplete .select2-results__option--highlighted[aria-selected] { + background-color: #79aec8; + color: white; +} + +.select2-container--admin-autocomplete .select2-results__group { + cursor: default; + display: block; + padding: 6px; +} diff --git a/src/dal_legacy_static/static/admin/css/vendor/select2/LICENSE-SELECT2.md b/src/dal_legacy_static/static/admin/css/vendor/select2/LICENSE-SELECT2.md new file mode 100755 index 000000000..86c7c291a --- /dev/null +++ b/src/dal_legacy_static/static/admin/css/vendor/select2/LICENSE-SELECT2.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2012-2015 Kevin Brown, Igor Vaynberg, and Select2 contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/src/dal_legacy_static/static/admin/css/vendor/select2/select2.css b/src/dal_legacy_static/static/admin/css/vendor/select2/select2.css new file mode 100755 index 000000000..10a111aab --- /dev/null +++ b/src/dal_legacy_static/static/admin/css/vendor/select2/select2.css @@ -0,0 +1,486 @@ +.select2-container { + box-sizing: border-box; + display: inline-block; + margin: 0; + position: relative; + vertical-align: middle; } + .select2-container .select2-selection--single { + box-sizing: border-box; + cursor: pointer; + display: block; + height: 28px; + user-select: none; + -webkit-user-select: none; } + .select2-container .select2-selection--single .select2-selection__rendered { + display: block; + padding-left: 8px; + padding-right: 20px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } + .select2-container .select2-selection--single .select2-selection__clear { + position: relative; } + .select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered { + padding-right: 8px; + padding-left: 20px; } + .select2-container .select2-selection--multiple { + box-sizing: border-box; + cursor: pointer; + display: block; + min-height: 32px; + user-select: none; + -webkit-user-select: none; } + .select2-container .select2-selection--multiple .select2-selection__rendered { + display: inline-block; + overflow: hidden; + padding-left: 8px; + text-overflow: ellipsis; + white-space: nowrap; } + .select2-container .select2-search--inline { + float: left; } + .select2-container .select2-search--inline .select2-search__field { + box-sizing: border-box; + border: none; + font-size: 100%; + margin-top: 5px; + padding: 0; } + .select2-container .select2-search--inline .select2-search__field::-webkit-search-cancel-button { + -webkit-appearance: none; } + +.select2-dropdown { + background-color: white; + border: 1px solid #aaa; + border-radius: 4px; + box-sizing: border-box; + display: block; + position: absolute; + left: -100000px; + width: 100%; + z-index: 1051; } + +.select2-results { + display: block; } + +.select2-results__options { + list-style: none; + margin: 0; + padding: 0; } + +.select2-results__option { + padding: 6px; + user-select: none; + -webkit-user-select: none; + color: #000; } + .select2-results__option[aria-selected] { + cursor: pointer; } + +.select2-container--open .select2-dropdown { + left: 0; } + +.select2-container--open .select2-dropdown--above { + border-bottom: none; + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; } + +.select2-container--open .select2-dropdown--below { + border-top: none; + border-top-left-radius: 0; + border-top-right-radius: 0; } + +.select2-search--dropdown { + display: block; + padding: 4px; } + .select2-search--dropdown .select2-search__field { + padding: 4px; + width: 100%; + box-sizing: border-box; } + .select2-search--dropdown .select2-search__field::-webkit-search-cancel-button { + -webkit-appearance: none; } + .select2-search--dropdown.select2-search--hide { + display: none; } + +.select2-close-mask { + border: 0; + margin: 0; + padding: 0; + display: block; + position: fixed; + left: 0; + top: 0; + min-height: 100%; + min-width: 100%; + height: auto; + width: auto; + opacity: 0; + z-index: 99; + background-color: #fff; + filter: alpha(opacity=0); } + +.select2-hidden-accessible { + border: 0 !important; + clip: rect(0 0 0 0) !important; + height: 1px !important; + margin: -1px !important; + overflow: hidden !important; + padding: 0 !important; + position: absolute !important; + width: 1px !important; } + +.select2-container--default .select2-selection--single { + background-color: #fff; + border: 1px solid #aaa; + border-radius: 4px; } + .select2-container--default .select2-selection--single .select2-selection__rendered { + color: #444; + line-height: 28px; } + .select2-container--default .select2-selection--single .select2-selection__clear { + cursor: pointer; + float: right; + font-weight: bold; } + .select2-container--default .select2-selection--single .select2-selection__placeholder { + color: #999; } + .select2-container--default .select2-selection--single .select2-selection__arrow { + height: 26px; + position: absolute; + top: 1px; + right: 1px; + width: 20px; } + .select2-container--default .select2-selection--single .select2-selection__arrow b { + border-color: #888 transparent transparent transparent; + border-style: solid; + border-width: 5px 4px 0 4px; + height: 0; + left: 50%; + margin-left: -4px; + margin-top: -2px; + position: absolute; + top: 50%; + width: 0; } + +.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__clear { + float: left; } + +.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__arrow { + left: 1px; + right: auto; } + +.select2-container--default.select2-container--disabled .select2-selection--single { + background-color: #eee; + cursor: default; } + .select2-container--default.select2-container--disabled .select2-selection--single .select2-selection__clear { + display: none; } + +.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b { + border-color: transparent transparent #888 transparent; + border-width: 0 4px 5px 4px; } + +.select2-container--default .select2-selection--multiple { + background-color: white; + border: 1px solid #aaa; + border-radius: 4px; + cursor: text; } + .select2-container--default .select2-selection--multiple .select2-selection__rendered { + box-sizing: border-box; + list-style: none; + margin: 0; + padding: 0 5px; + width: 100%; } + .select2-container--default .select2-selection--multiple .select2-selection__rendered li { + list-style: none; } + .select2-container--default .select2-selection--multiple .select2-selection__placeholder { + color: #999; + margin-top: 5px; + float: left; } + .select2-container--default .select2-selection--multiple .select2-selection__clear { + cursor: pointer; + float: right; + font-weight: bold; + margin-top: 5px; + margin-right: 10px; } + .select2-container--default .select2-selection--multiple .select2-selection__choice { + background-color: #e4e4e4; + border: 1px solid #aaa; + border-radius: 4px; + cursor: default; + float: left; + margin-right: 5px; + margin-top: 5px; + padding: 0 5px; + color: #000; } + .select2-container--default .select2-selection--multiple .select2-selection__choice__remove { + color: #999; + cursor: pointer; + display: inline-block; + font-weight: bold; + margin-right: 2px; } + .select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover { + color: #333; } + +.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice, .select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__placeholder, .select2-container--default[dir="rtl"] .select2-selection--multiple .select2-search--inline { + float: right; } + +.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice { + margin-left: 5px; + margin-right: auto; } + +.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove { + margin-left: 2px; + margin-right: auto; } + +.select2-container--default.select2-container--focus .select2-selection--multiple { + border: solid black 1px; + outline: 0; } + +.select2-container--default.select2-container--disabled .select2-selection--multiple { + background-color: #eee; + cursor: default; } + +.select2-container--default.select2-container--disabled .select2-selection__choice__remove { + display: none; } + +.select2-container--default.select2-container--open.select2-container--above .select2-selection--single, .select2-container--default.select2-container--open.select2-container--above .select2-selection--multiple { + border-top-left-radius: 0; + border-top-right-radius: 0; } + +.select2-container--default.select2-container--open.select2-container--below .select2-selection--single, .select2-container--default.select2-container--open.select2-container--below .select2-selection--multiple { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; } + +.select2-container--default .select2-search--dropdown .select2-search__field { + border: 1px solid #aaa; } + +.select2-container--default .select2-search--inline .select2-search__field { + background: transparent; + border: none; + outline: 0; + box-shadow: none; + -webkit-appearance: textfield; } + +.select2-container--default .select2-results > .select2-results__options { + max-height: 200px; + overflow-y: auto; } + +.select2-container--default .select2-results__option[role=group] { + padding: 0; } + +.select2-container--default .select2-results__option[aria-disabled=true] { + color: #999; } + +.select2-container--default .select2-results__option[aria-selected=true] { + background-color: #ddd; } + +.select2-container--default .select2-results__option .select2-results__option { + padding-left: 1em; } + .select2-container--default .select2-results__option .select2-results__option .select2-results__group { + padding-left: 0; } + .select2-container--default .select2-results__option .select2-results__option .select2-results__option { + margin-left: -1em; + padding-left: 2em; } + .select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option { + margin-left: -2em; + padding-left: 3em; } + .select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option { + margin-left: -3em; + padding-left: 4em; } + .select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option { + margin-left: -4em; + padding-left: 5em; } + .select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option { + margin-left: -5em; + padding-left: 6em; } + +.select2-container--default .select2-results__option--highlighted[aria-selected] { + background-color: #5897fb; + color: white; } + +.select2-container--default .select2-results__group { + cursor: default; + display: block; + padding: 6px; } + +.select2-container--classic .select2-selection--single { + background-color: #f7f7f7; + border: 1px solid #aaa; + border-radius: 4px; + outline: 0; + background-image: -webkit-linear-gradient(top, white 50%, #eeeeee 100%); + background-image: -o-linear-gradient(top, white 50%, #eeeeee 100%); + background-image: linear-gradient(to bottom, white 50%, #eeeeee 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0); } + .select2-container--classic .select2-selection--single:focus { + border: 1px solid #5897fb; } + .select2-container--classic .select2-selection--single .select2-selection__rendered { + color: #444; + line-height: 28px; } + .select2-container--classic .select2-selection--single .select2-selection__clear { + cursor: pointer; + float: right; + font-weight: bold; + margin-right: 10px; } + .select2-container--classic .select2-selection--single .select2-selection__placeholder { + color: #999; } + .select2-container--classic .select2-selection--single .select2-selection__arrow { + background-color: #ddd; + border: none; + border-left: 1px solid #aaa; + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; + height: 26px; + position: absolute; + top: 1px; + right: 1px; + width: 20px; + background-image: -webkit-linear-gradient(top, #eeeeee 50%, #cccccc 100%); + background-image: -o-linear-gradient(top, #eeeeee 50%, #cccccc 100%); + background-image: linear-gradient(to bottom, #eeeeee 50%, #cccccc 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFCCCCCC', GradientType=0); } + .select2-container--classic .select2-selection--single .select2-selection__arrow b { + border-color: #888 transparent transparent transparent; + border-style: solid; + border-width: 5px 4px 0 4px; + height: 0; + left: 50%; + margin-left: -4px; + margin-top: -2px; + position: absolute; + top: 50%; + width: 0; } + +.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__clear { + float: left; } + +.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__arrow { + border: none; + border-right: 1px solid #aaa; + border-radius: 0; + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; + left: 1px; + right: auto; } + +.select2-container--classic.select2-container--open .select2-selection--single { + border: 1px solid #5897fb; } + .select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow { + background: transparent; + border: none; } + .select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow b { + border-color: transparent transparent #888 transparent; + border-width: 0 4px 5px 4px; } + +.select2-container--classic.select2-container--open.select2-container--above .select2-selection--single { + border-top: none; + border-top-left-radius: 0; + border-top-right-radius: 0; + background-image: -webkit-linear-gradient(top, white 0%, #eeeeee 50%); + background-image: -o-linear-gradient(top, white 0%, #eeeeee 50%); + background-image: linear-gradient(to bottom, white 0%, #eeeeee 50%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0); } + +.select2-container--classic.select2-container--open.select2-container--below .select2-selection--single { + border-bottom: none; + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + background-image: -webkit-linear-gradient(top, #eeeeee 50%, white 100%); + background-image: -o-linear-gradient(top, #eeeeee 50%, white 100%); + background-image: linear-gradient(to bottom, #eeeeee 50%, white 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFFFFFFF', GradientType=0); } + +.select2-container--classic .select2-selection--multiple { + background-color: white; + border: 1px solid #aaa; + border-radius: 4px; + cursor: text; + outline: 0; } + .select2-container--classic .select2-selection--multiple:focus { + border: 1px solid #5897fb; } + .select2-container--classic .select2-selection--multiple .select2-selection__rendered { + list-style: none; + margin: 0; + padding: 0 5px; } + .select2-container--classic .select2-selection--multiple .select2-selection__clear { + display: none; } + .select2-container--classic .select2-selection--multiple .select2-selection__choice { + background-color: #e4e4e4; + border: 1px solid #aaa; + border-radius: 4px; + cursor: default; + float: left; + margin-right: 5px; + margin-top: 5px; + padding: 0 5px; } + .select2-container--classic .select2-selection--multiple .select2-selection__choice__remove { + color: #888; + cursor: pointer; + display: inline-block; + font-weight: bold; + margin-right: 2px; } + .select2-container--classic .select2-selection--multiple .select2-selection__choice__remove:hover { + color: #555; } + +.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice { + float: right; } + +.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice { + margin-left: 5px; + margin-right: auto; } + +.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove { + margin-left: 2px; + margin-right: auto; } + +.select2-container--classic.select2-container--open .select2-selection--multiple { + border: 1px solid #5897fb; } + +.select2-container--classic.select2-container--open.select2-container--above .select2-selection--multiple { + border-top: none; + border-top-left-radius: 0; + border-top-right-radius: 0; } + +.select2-container--classic.select2-container--open.select2-container--below .select2-selection--multiple { + border-bottom: none; + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; } + +.select2-container--classic .select2-search--dropdown .select2-search__field { + border: 1px solid #aaa; + outline: 0; } + +.select2-container--classic .select2-search--inline .select2-search__field { + outline: 0; + box-shadow: none; } + +.select2-container--classic .select2-dropdown { + background-color: white; + border: 1px solid transparent; } + +.select2-container--classic .select2-dropdown--above { + border-bottom: none; } + +.select2-container--classic .select2-dropdown--below { + border-top: none; } + +.select2-container--classic .select2-results > .select2-results__options { + max-height: 200px; + overflow-y: auto; } + +.select2-container--classic .select2-results__option[role=group] { + padding: 0; } + +.select2-container--classic .select2-results__option[aria-disabled=true] { + color: grey; } + +.select2-container--classic .select2-results__option--highlighted[aria-selected] { + background-color: #3875d7; + color: white; } + +.select2-container--classic .select2-results__group { + cursor: default; + display: block; + padding: 6px; } + +.select2-container--classic.select2-container--open .select2-dropdown { + border-color: #5897fb; } diff --git a/src/dal_legacy_static/static/admin/css/vendor/select2/select2.min.css b/src/dal_legacy_static/static/admin/css/vendor/select2/select2.min.css new file mode 100755 index 000000000..76de04d92 --- /dev/null +++ b/src/dal_legacy_static/static/admin/css/vendor/select2/select2.min.css @@ -0,0 +1 @@ +.select2-container{box-sizing:border-box;display:inline-block;margin:0;position:relative;vertical-align:middle}.select2-container .select2-selection--single{box-sizing:border-box;cursor:pointer;display:block;height:28px;user-select:none;-webkit-user-select:none}.select2-container .select2-selection--single .select2-selection__rendered{display:block;padding-left:8px;padding-right:20px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.select2-container .select2-selection--single .select2-selection__clear{position:relative}.select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered{padding-right:8px;padding-left:20px}.select2-container .select2-selection--multiple{box-sizing:border-box;cursor:pointer;display:block;min-height:32px;user-select:none;-webkit-user-select:none}.select2-container .select2-selection--multiple .select2-selection__rendered{display:inline-block;overflow:hidden;padding-left:8px;text-overflow:ellipsis;white-space:nowrap}.select2-container .select2-search--inline{float:left}.select2-container .select2-search--inline .select2-search__field{box-sizing:border-box;border:none;font-size:100%;margin-top:5px;padding:0}.select2-container .select2-search--inline .select2-search__field::-webkit-search-cancel-button{-webkit-appearance:none}.select2-dropdown{background-color:white;border:1px solid #aaa;border-radius:4px;box-sizing:border-box;display:block;position:absolute;left:-100000px;width:100%;z-index:1051}.select2-results{display:block}.select2-results__options{list-style:none;margin:0;padding:0}.select2-results__option{padding:6px;user-select:none;-webkit-user-select:none}.select2-results__option[aria-selected]{cursor:pointer}.select2-container--open .select2-dropdown{left:0}.select2-container--open .select2-dropdown--above{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--open .select2-dropdown--below{border-top:none;border-top-left-radius:0;border-top-right-radius:0}.select2-search--dropdown{display:block;padding:4px}.select2-search--dropdown .select2-search__field{padding:4px;width:100%;box-sizing:border-box}.select2-search--dropdown .select2-search__field::-webkit-search-cancel-button{-webkit-appearance:none}.select2-search--dropdown.select2-search--hide{display:none}.select2-close-mask{border:0;margin:0;padding:0;display:block;position:fixed;left:0;top:0;min-height:100%;min-width:100%;height:auto;width:auto;opacity:0;z-index:99;background-color:#fff;filter:alpha(opacity=0)}.select2-hidden-accessible{border:0 !important;clip:rect(0 0 0 0) !important;height:1px !important;margin:-1px !important;overflow:hidden !important;padding:0 !important;position:absolute !important;width:1px !important}.select2-container--default .select2-selection--single{background-color:#fff;border:1px solid #aaa;border-radius:4px}.select2-container--default .select2-selection--single .select2-selection__rendered{color:#444;line-height:28px}.select2-container--default .select2-selection--single .select2-selection__clear{cursor:pointer;float:right;font-weight:bold}.select2-container--default .select2-selection--single .select2-selection__placeholder{color:#999}.select2-container--default .select2-selection--single .select2-selection__arrow{height:26px;position:absolute;top:1px;right:1px;width:20px}.select2-container--default .select2-selection--single .select2-selection__arrow b{border-color:#888 transparent transparent transparent;border-style:solid;border-width:5px 4px 0 4px;height:0;left:50%;margin-left:-4px;margin-top:-2px;position:absolute;top:50%;width:0}.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__clear{float:left}.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__arrow{left:1px;right:auto}.select2-container--default.select2-container--disabled .select2-selection--single{background-color:#eee;cursor:default}.select2-container--default.select2-container--disabled .select2-selection--single .select2-selection__clear{display:none}.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b{border-color:transparent transparent #888 transparent;border-width:0 4px 5px 4px}.select2-container--default .select2-selection--multiple{background-color:white;border:1px solid #aaa;border-radius:4px;cursor:text}.select2-container--default .select2-selection--multiple .select2-selection__rendered{box-sizing:border-box;list-style:none;margin:0;padding:0 5px;width:100%}.select2-container--default .select2-selection--multiple .select2-selection__rendered li{list-style:none}.select2-container--default .select2-selection--multiple .select2-selection__placeholder{color:#999;margin-top:5px;float:left}.select2-container--default .select2-selection--multiple .select2-selection__clear{cursor:pointer;float:right;font-weight:bold;margin-top:5px;margin-right:10px}.select2-container--default .select2-selection--multiple .select2-selection__choice{background-color:#e4e4e4;border:1px solid #aaa;border-radius:4px;cursor:default;float:left;margin-right:5px;margin-top:5px;padding:0 5px}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove{color:#999;cursor:pointer;display:inline-block;font-weight:bold;margin-right:2px}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover{color:#333}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice,.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__placeholder,.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-search--inline{float:right}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice{margin-left:5px;margin-right:auto}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove{margin-left:2px;margin-right:auto}.select2-container--default.select2-container--focus .select2-selection--multiple{border:solid black 1px;outline:0}.select2-container--default.select2-container--disabled .select2-selection--multiple{background-color:#eee;cursor:default}.select2-container--default.select2-container--disabled .select2-selection__choice__remove{display:none}.select2-container--default.select2-container--open.select2-container--above .select2-selection--single,.select2-container--default.select2-container--open.select2-container--above .select2-selection--multiple{border-top-left-radius:0;border-top-right-radius:0}.select2-container--default.select2-container--open.select2-container--below .select2-selection--single,.select2-container--default.select2-container--open.select2-container--below .select2-selection--multiple{border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--default .select2-search--dropdown .select2-search__field{border:1px solid #aaa}.select2-container--default .select2-search--inline .select2-search__field{background:transparent;border:none;outline:0;box-shadow:none;-webkit-appearance:textfield}.select2-container--default .select2-results>.select2-results__options{max-height:200px;overflow-y:auto}.select2-container--default .select2-results__option[role=group]{padding:0}.select2-container--default .select2-results__option[aria-disabled=true]{color:#999}.select2-container--default .select2-results__option[aria-selected=true]{background-color:#ddd}.select2-container--default .select2-results__option .select2-results__option{padding-left:1em}.select2-container--default .select2-results__option .select2-results__option .select2-results__group{padding-left:0}.select2-container--default .select2-results__option .select2-results__option .select2-results__option{margin-left:-1em;padding-left:2em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-2em;padding-left:3em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-3em;padding-left:4em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-4em;padding-left:5em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-5em;padding-left:6em}.select2-container--default .select2-results__option--highlighted[aria-selected]{background-color:#5897fb;color:white}.select2-container--default .select2-results__group{cursor:default;display:block;padding:6px}.select2-container--classic .select2-selection--single{background-color:#f7f7f7;border:1px solid #aaa;border-radius:4px;outline:0;background-image:-webkit-linear-gradient(top, #fff 50%, #eee 100%);background-image:-o-linear-gradient(top, #fff 50%, #eee 100%);background-image:linear-gradient(to bottom, #fff 50%, #eee 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0)}.select2-container--classic .select2-selection--single:focus{border:1px solid #5897fb}.select2-container--classic .select2-selection--single .select2-selection__rendered{color:#444;line-height:28px}.select2-container--classic .select2-selection--single .select2-selection__clear{cursor:pointer;float:right;font-weight:bold;margin-right:10px}.select2-container--classic .select2-selection--single .select2-selection__placeholder{color:#999}.select2-container--classic .select2-selection--single .select2-selection__arrow{background-color:#ddd;border:none;border-left:1px solid #aaa;border-top-right-radius:4px;border-bottom-right-radius:4px;height:26px;position:absolute;top:1px;right:1px;width:20px;background-image:-webkit-linear-gradient(top, #eee 50%, #ccc 100%);background-image:-o-linear-gradient(top, #eee 50%, #ccc 100%);background-image:linear-gradient(to bottom, #eee 50%, #ccc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFCCCCCC', GradientType=0)}.select2-container--classic .select2-selection--single .select2-selection__arrow b{border-color:#888 transparent transparent transparent;border-style:solid;border-width:5px 4px 0 4px;height:0;left:50%;margin-left:-4px;margin-top:-2px;position:absolute;top:50%;width:0}.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__clear{float:left}.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__arrow{border:none;border-right:1px solid #aaa;border-radius:0;border-top-left-radius:4px;border-bottom-left-radius:4px;left:1px;right:auto}.select2-container--classic.select2-container--open .select2-selection--single{border:1px solid #5897fb}.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow{background:transparent;border:none}.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow b{border-color:transparent transparent #888 transparent;border-width:0 4px 5px 4px}.select2-container--classic.select2-container--open.select2-container--above .select2-selection--single{border-top:none;border-top-left-radius:0;border-top-right-radius:0;background-image:-webkit-linear-gradient(top, #fff 0%, #eee 50%);background-image:-o-linear-gradient(top, #fff 0%, #eee 50%);background-image:linear-gradient(to bottom, #fff 0%, #eee 50%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0)}.select2-container--classic.select2-container--open.select2-container--below .select2-selection--single{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0;background-image:-webkit-linear-gradient(top, #eee 50%, #fff 100%);background-image:-o-linear-gradient(top, #eee 50%, #fff 100%);background-image:linear-gradient(to bottom, #eee 50%, #fff 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFFFFFFF', GradientType=0)}.select2-container--classic .select2-selection--multiple{background-color:white;border:1px solid #aaa;border-radius:4px;cursor:text;outline:0}.select2-container--classic .select2-selection--multiple:focus{border:1px solid #5897fb}.select2-container--classic .select2-selection--multiple .select2-selection__rendered{list-style:none;margin:0;padding:0 5px}.select2-container--classic .select2-selection--multiple .select2-selection__clear{display:none}.select2-container--classic .select2-selection--multiple .select2-selection__choice{background-color:#e4e4e4;border:1px solid #aaa;border-radius:4px;cursor:default;float:left;margin-right:5px;margin-top:5px;padding:0 5px}.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove{color:#888;cursor:pointer;display:inline-block;font-weight:bold;margin-right:2px}.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove:hover{color:#555}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice{float:right}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice{margin-left:5px;margin-right:auto}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove{margin-left:2px;margin-right:auto}.select2-container--classic.select2-container--open .select2-selection--multiple{border:1px solid #5897fb}.select2-container--classic.select2-container--open.select2-container--above .select2-selection--multiple{border-top:none;border-top-left-radius:0;border-top-right-radius:0}.select2-container--classic.select2-container--open.select2-container--below .select2-selection--multiple{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--classic .select2-search--dropdown .select2-search__field{border:1px solid #aaa;outline:0}.select2-container--classic .select2-search--inline .select2-search__field{outline:0;box-shadow:none}.select2-container--classic .select2-dropdown{background-color:#fff;border:1px solid transparent}.select2-container--classic .select2-dropdown--above{border-bottom:none}.select2-container--classic .select2-dropdown--below{border-top:none}.select2-container--classic .select2-results>.select2-results__options{max-height:200px;overflow-y:auto}.select2-container--classic .select2-results__option[role=group]{padding:0}.select2-container--classic .select2-results__option[aria-disabled=true]{color:grey}.select2-container--classic .select2-results__option--highlighted[aria-selected]{background-color:#3875d7;color:#fff}.select2-container--classic .select2-results__group{cursor:default;display:block;padding:6px}.select2-container--classic.select2-container--open .select2-dropdown{border-color:#5897fb} diff --git a/src/dal_legacy_static/static/admin/js/autocomplete.js b/src/dal_legacy_static/static/admin/js/autocomplete.js new file mode 100644 index 000000000..65c0702dd --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/autocomplete.js @@ -0,0 +1,37 @@ +(function($) { + 'use strict'; + var init = function($element, options) { + var settings = $.extend({ + ajax: { + data: function(params) { + return { + term: params.term, + page: params.page + }; + } + } + }, options); + $element.select2(settings); + }; + + $.fn.djangoAdminSelect2 = function(options) { + var settings = $.extend({}, options); + $.each(this, function(i, element) { + var $element = $(element); + init($element, settings); + }); + return this; + }; + + $(function() { + // Initialize all autocomplete widgets except the one in the template + // form used when a new formset is added. + $('.admin-autocomplete').not('[name*=__prefix__]').djangoAdminSelect2(); + }); + + $(document).on('formset:added', (function() { + return function(event, $newFormset) { + return $newFormset.find('.admin-autocomplete').djangoAdminSelect2(); + }; + })(this)); +}(django.jQuery)); diff --git a/src/dal_legacy_static/static/admin/js/vendor/select2/LICENSE.md b/src/dal_legacy_static/static/admin/js/vendor/select2/LICENSE.md new file mode 100755 index 000000000..86c7c291a --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/vendor/select2/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2012-2015 Kevin Brown, Igor Vaynberg, and Select2 contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/ar.js b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/ar.js new file mode 100755 index 000000000..01a688294 --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/ar.js @@ -0,0 +1,3 @@ +/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ + +(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/ar",[],function(){return{errorLoading:function(){return"لا يمكن تحميل النتائج"},inputTooLong:function(e){var t=e.input.length-e.maximum,n="الرجاء حذف "+t+" عناصر";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="الرجاء إضافة "+t+" عناصر";return n},loadingMore:function(){return"جاري تحميل نتائج إضافية..."},maximumSelected:function(e){var t="تستطيع إختيار "+e.maximum+" بنود فقط";return t},noResults:function(){return"لم يتم العثور على أي نتائج"},searching:function(){return"جاري البحث…"}}}),{define:e.define,require:e.require}})(); \ No newline at end of file diff --git a/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/az.js b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/az.js new file mode 100755 index 000000000..2accb973f --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/az.js @@ -0,0 +1,3 @@ +/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ + +(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/az",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum;return t+" simvol silin"},inputTooShort:function(e){var t=e.minimum-e.input.length;return t+" simvol daxil edin"},loadingMore:function(){return"Daha çox nəticə yüklənir…"},maximumSelected:function(e){return"Sadəcə "+e.maximum+" element seçə bilərsiniz"},noResults:function(){return"Nəticə tapılmadı"},searching:function(){return"Axtarılır…"}}}),{define:e.define,require:e.require}})(); \ No newline at end of file diff --git a/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/bg.js b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/bg.js new file mode 100755 index 000000000..35ae98944 --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/bg.js @@ -0,0 +1,3 @@ +/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ + +(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/bg",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n="Моля въведете с "+t+" по-малко символ";return t>1&&(n+="a"),n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Моля въведете още "+t+" символ";return t>1&&(n+="a"),n},loadingMore:function(){return"Зареждат се още…"},maximumSelected:function(e){var t="Можете да направите до "+e.maximum+" ";return e.maximum>1?t+="избора":t+="избор",t},noResults:function(){return"Няма намерени съвпадения"},searching:function(){return"Търсене…"}}}),{define:e.define,require:e.require}})(); \ No newline at end of file diff --git a/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/ca.js b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/ca.js new file mode 100755 index 000000000..fdb5f3d2a --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/ca.js @@ -0,0 +1,3 @@ +/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ + +(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/ca",[],function(){return{errorLoading:function(){return"La càrrega ha fallat"},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Si us plau, elimina "+t+" car";return t==1?n+="àcter":n+="àcters",n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Si us plau, introdueix "+t+" car";return t==1?n+="àcter":n+="àcters",n},loadingMore:function(){return"Carregant més resultats…"},maximumSelected:function(e){var t="Només es pot seleccionar "+e.maximum+" element";return e.maximum!=1&&(t+="s"),t},noResults:function(){return"No s'han trobat resultats"},searching:function(){return"Cercant…"}}}),{define:e.define,require:e.require}})(); \ No newline at end of file diff --git a/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/cs.js b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/cs.js new file mode 100755 index 000000000..9651378a6 --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/cs.js @@ -0,0 +1,3 @@ +/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ + +(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/cs",[],function(){function e(e,t){switch(e){case 2:return t?"dva":"dvě";case 3:return"tři";case 4:return"čtyři"}return""}return{errorLoading:function(){return"Výsledky nemohly být načteny."},inputTooLong:function(t){var n=t.input.length-t.maximum;return n==1?"Prosím zadejte o jeden znak méně":n<=4?"Prosím zadejte o "+e(n,!0)+" znaky méně":"Prosím zadejte o "+n+" znaků méně"},inputTooShort:function(t){var n=t.minimum-t.input.length;return n==1?"Prosím zadejte ještě jeden znak":n<=4?"Prosím zadejte ještě další "+e(n,!0)+" znaky":"Prosím zadejte ještě dalších "+n+" znaků"},loadingMore:function(){return"Načítají se další výsledky…"},maximumSelected:function(t){var n=t.maximum;return n==1?"Můžete zvolit jen jednu položku":n<=4?"Můžete zvolit maximálně "+e(n,!1)+" položky":"Můžete zvolit maximálně "+n+" položek"},noResults:function(){return"Nenalezeny žádné položky"},searching:function(){return"Vyhledávání…"}}}),{define:e.define,require:e.require}})(); \ No newline at end of file diff --git a/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/da.js b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/da.js new file mode 100755 index 000000000..501c51e93 --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/da.js @@ -0,0 +1,3 @@ +/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ + +(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/da",[],function(){return{errorLoading:function(){return"Resultaterne kunne ikke indlæses."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Angiv venligst "+t+" tegn mindre";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Angiv venligst "+t+" tegn mere";return n},loadingMore:function(){return"Indlæser flere resultater…"},maximumSelected:function(e){var t="Du kan kun vælge "+e.maximum+" emne";return e.maximum!=1&&(t+="r"),t},noResults:function(){return"Ingen resultater fundet"},searching:function(){return"Søger…"}}}),{define:e.define,require:e.require}})(); \ No newline at end of file diff --git a/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/de.js b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/de.js new file mode 100755 index 000000000..9a6d55366 --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/de.js @@ -0,0 +1,3 @@ +/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ + +(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/de",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum;return"Bitte "+t+" Zeichen weniger eingeben"},inputTooShort:function(e){var t=e.minimum-e.input.length;return"Bitte "+t+" Zeichen mehr eingeben"},loadingMore:function(){return"Lade mehr Ergebnisse…"},maximumSelected:function(e){var t="Sie können nur "+e.maximum+" Eintr";return e.maximum===1?t+="ag":t+="äge",t+=" auswählen",t},noResults:function(){return"Keine Übereinstimmungen gefunden"},searching:function(){return"Suche…"}}}),{define:e.define,require:e.require}})(); \ No newline at end of file diff --git a/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/el.js b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/el.js new file mode 100755 index 000000000..4735d1405 --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/el.js @@ -0,0 +1,3 @@ +/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ + +(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/el",[],function(){return{errorLoading:function(){return"Τα αποτελέσματα δεν μπόρεσαν να φορτώσουν."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Παρακαλώ διαγράψτε "+t+" χαρακτήρ";return t==1&&(n+="α"),t!=1&&(n+="ες"),n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Παρακαλώ συμπληρώστε "+t+" ή περισσότερους χαρακτήρες";return n},loadingMore:function(){return"Φόρτωση περισσότερων αποτελεσμάτων…"},maximumSelected:function(e){var t="Μπορείτε να επιλέξετε μόνο "+e.maximum+" επιλογ";return e.maximum==1&&(t+="ή"),e.maximum!=1&&(t+="ές"),t},noResults:function(){return"Δεν βρέθηκαν αποτελέσματα"},searching:function(){return"Αναζήτηση…"}}}),{define:e.define,require:e.require}})(); \ No newline at end of file diff --git a/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/en.js b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/en.js new file mode 100755 index 000000000..8e80ede8d --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/en.js @@ -0,0 +1,3 @@ +/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ + +(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/en",[],function(){return{errorLoading:function(){return"The results could not be loaded."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Please delete "+t+" character";return t!=1&&(n+="s"),n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Please enter "+t+" or more characters";return n},loadingMore:function(){return"Loading more results…"},maximumSelected:function(e){var t="You can only select "+e.maximum+" item";return e.maximum!=1&&(t+="s"),t},noResults:function(){return"No results found"},searching:function(){return"Searching…"}}}),{define:e.define,require:e.require}})(); \ No newline at end of file diff --git a/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/es.js b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/es.js new file mode 100755 index 000000000..0a096502d --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/es.js @@ -0,0 +1,3 @@ +/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ + +(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/es",[],function(){return{errorLoading:function(){return"La carga falló"},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Por favor, elimine "+t+" car";return t==1?n+="ácter":n+="acteres",n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Por favor, introduzca "+t+" car";return t==1?n+="ácter":n+="acteres",n},loadingMore:function(){return"Cargando más resultados…"},maximumSelected:function(e){var t="Sólo puede seleccionar "+e.maximum+" elemento";return e.maximum!=1&&(t+="s"),t},noResults:function(){return"No se encontraron resultados"},searching:function(){return"Buscando…"}}}),{define:e.define,require:e.require}})(); \ No newline at end of file diff --git a/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/et.js b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/et.js new file mode 100755 index 000000000..c70f4a5b3 --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/et.js @@ -0,0 +1,3 @@ +/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ + +(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/et",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n="Sisesta "+t+" täht";return t!=1&&(n+="e"),n+=" vähem",n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Sisesta "+t+" täht";return t!=1&&(n+="e"),n+=" rohkem",n},loadingMore:function(){return"Laen tulemusi…"},maximumSelected:function(e){var t="Saad vaid "+e.maximum+" tulemus";return e.maximum==1?t+="e":t+="t",t+=" valida",t},noResults:function(){return"Tulemused puuduvad"},searching:function(){return"Otsin…"}}}),{define:e.define,require:e.require}})(); \ No newline at end of file diff --git a/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/eu.js b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/eu.js new file mode 100755 index 000000000..9336053a7 --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/eu.js @@ -0,0 +1,3 @@ +/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ + +(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/eu",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n="Idatzi ";return t==1?n+="karaktere bat":n+=t+" karaktere",n+=" gutxiago",n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Idatzi ";return t==1?n+="karaktere bat":n+=t+" karaktere",n+=" gehiago",n},loadingMore:function(){return"Emaitza gehiago kargatzen…"},maximumSelected:function(e){return e.maximum===1?"Elementu bakarra hauta dezakezu":e.maximum+" elementu hauta ditzakezu soilik"},noResults:function(){return"Ez da bat datorrenik aurkitu"},searching:function(){return"Bilatzen…"}}}),{define:e.define,require:e.require}})(); \ No newline at end of file diff --git a/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/fa.js b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/fa.js new file mode 100755 index 000000000..5118cd28f --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/fa.js @@ -0,0 +1,3 @@ +/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ + +(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/fa",[],function(){return{errorLoading:function(){return"امکان بارگذاری نتایج وجود ندارد."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="لطفاً "+t+" کاراکتر را حذف نمایید";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="لطفاً تعداد "+t+" کاراکتر یا بیشتر وارد نمایید";return n},loadingMore:function(){return"در حال بارگذاری نتایج بیشتر..."},maximumSelected:function(e){var t="شما تنها می‌توانید "+e.maximum+" آیتم را انتخاب نمایید";return t},noResults:function(){return"هیچ نتیجه‌ای یافت نشد"},searching:function(){return"در حال جستجو..."}}}),{define:e.define,require:e.require}})(); \ No newline at end of file diff --git a/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/fi.js b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/fi.js new file mode 100755 index 000000000..9e60f26a0 --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/fi.js @@ -0,0 +1,3 @@ +/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ + +(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/fi",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum;return"Ole hyvä ja anna "+t+" merkkiä vähemmän"},inputTooShort:function(e){var t=e.minimum-e.input.length;return"Ole hyvä ja anna "+t+" merkkiä lisää"},loadingMore:function(){return"Ladataan lisää tuloksia…"},maximumSelected:function(e){return"Voit valita ainoastaan "+e.maximum+" kpl"},noResults:function(){return"Ei tuloksia"},searching:function(){}}}),{define:e.define,require:e.require}})(); \ No newline at end of file diff --git a/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/fr.js b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/fr.js new file mode 100755 index 000000000..e4a665009 --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/fr.js @@ -0,0 +1,3 @@ +/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ + +(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/fr",[],function(){return{errorLoading:function(){return"Les résultats ne peuvent pas être chargés."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Supprimez "+t+" caractère";return t!==1&&(n+="s"),n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Saisissez "+t+" caractère";return t!==1&&(n+="s"),n},loadingMore:function(){return"Chargement de résultats supplémentaires…"},maximumSelected:function(e){var t="Vous pouvez seulement sélectionner "+e.maximum+" élément";return e.maximum!==1&&(t+="s"),t},noResults:function(){return"Aucun résultat trouvé"},searching:function(){return"Recherche en cours…"}}}),{define:e.define,require:e.require}})(); \ No newline at end of file diff --git a/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/gl.js b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/gl.js new file mode 100755 index 000000000..02f258f92 --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/gl.js @@ -0,0 +1,3 @@ +/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ + +(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/gl",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n="Elimine ";return t===1?n+="un carácter":n+=t+" caracteres",n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Engada ";return t===1?n+="un carácter":n+=t+" caracteres",n},loadingMore:function(){return"Cargando máis resultados…"},maximumSelected:function(e){var t="Só pode ";return e.maximum===1?t+="un elemento":t+=e.maximum+" elementos",t},noResults:function(){return"Non se atoparon resultados"},searching:function(){return"Buscando…"}}}),{define:e.define,require:e.require}})(); \ No newline at end of file diff --git a/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/he.js b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/he.js new file mode 100755 index 000000000..881f8d389 --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/he.js @@ -0,0 +1,3 @@ +/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ + +(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/he",[],function(){return{errorLoading:function(){return"שגיאה בטעינת התוצאות"},inputTooLong:function(e){var t=e.input.length-e.maximum,n="נא למחוק ";return t===1?n+="תו אחד":n+=t+" תווים",n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="נא להכניס ";return t===1?n+="תו אחד":n+=t+" תווים",n+=" או יותר",n},loadingMore:function(){return"טוען תוצאות נוספות…"},maximumSelected:function(e){var t="באפשרותך לבחור עד ";return e.maximum===1?t+="פריט אחד":t+=e.maximum+" פריטים",t},noResults:function(){return"לא נמצאו תוצאות"},searching:function(){return"מחפש…"}}}),{define:e.define,require:e.require}})(); \ No newline at end of file diff --git a/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/hi.js b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/hi.js new file mode 100755 index 000000000..e82968426 --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/hi.js @@ -0,0 +1,3 @@ +/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ + +(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/hi",[],function(){return{errorLoading:function(){return"परिणामों को लोड नहीं किया जा सका।"},inputTooLong:function(e){var t=e.input.length-e.maximum,n=t+" अक्षर को हटा दें";return t>1&&(n=t+" अक्षरों को हटा दें "),n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="कृपया "+t+" या अधिक अक्षर दर्ज करें";return n},loadingMore:function(){return"अधिक परिणाम लोड हो रहे है..."},maximumSelected:function(e){var t="आप केवल "+e.maximum+" आइटम का चयन कर सकते हैं";return t},noResults:function(){return"कोई परिणाम नहीं मिला"},searching:function(){return"खोज रहा है..."}}}),{define:e.define,require:e.require}})(); \ No newline at end of file diff --git a/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/hr.js b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/hr.js new file mode 100755 index 000000000..89f7b12bf --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/hr.js @@ -0,0 +1,3 @@ +/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ + +(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/hr",[],function(){function e(e){var t=" "+e+" znak";return e%10<5&&e%10>0&&(e%100<5||e%100>19)?e%10>1&&(t+="a"):t+="ova",t}return{errorLoading:function(){return"Preuzimanje nije uspjelo."},inputTooLong:function(t){var n=t.input.length-t.maximum;return"Unesite "+e(n)},inputTooShort:function(t){var n=t.minimum-t.input.length;return"Unesite još "+e(n)},loadingMore:function(){return"Učitavanje rezultata…"},maximumSelected:function(e){return"Maksimalan broj odabranih stavki je "+e.maximum},noResults:function(){return"Nema rezultata"},searching:function(){return"Pretraga…"}}}),{define:e.define,require:e.require}})(); \ No newline at end of file diff --git a/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/hu.js b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/hu.js new file mode 100755 index 000000000..74c8a90de --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/hu.js @@ -0,0 +1,3 @@ +/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ + +(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/hu",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum;return"Túl hosszú. "+t+" karakterrel több, mint kellene."},inputTooShort:function(e){var t=e.minimum-e.input.length;return"Túl rövid. Még "+t+" karakter hiányzik."},loadingMore:function(){return"Töltés…"},maximumSelected:function(e){return"Csak "+e.maximum+" elemet lehet kiválasztani."},noResults:function(){return"Nincs találat."},searching:function(){return"Keresés…"}}}),{define:e.define,require:e.require}})(); \ No newline at end of file diff --git a/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/id.js b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/id.js new file mode 100755 index 000000000..958678261 --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/id.js @@ -0,0 +1,3 @@ +/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ + +(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/id",[],function(){return{errorLoading:function(){return"Data tidak boleh diambil."},inputTooLong:function(e){var t=e.input.length-e.maximum;return"Hapuskan "+t+" huruf"},inputTooShort:function(e){var t=e.minimum-e.input.length;return"Masukkan "+t+" huruf lagi"},loadingMore:function(){return"Mengambil data…"},maximumSelected:function(e){return"Anda hanya dapat memilih "+e.maximum+" pilihan"},noResults:function(){return"Tidak ada data yang sesuai"},searching:function(){return"Mencari…"}}}),{define:e.define,require:e.require}})(); \ No newline at end of file diff --git a/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/is.js b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/is.js new file mode 100755 index 000000000..ab97a14d1 --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/is.js @@ -0,0 +1,3 @@ +/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ + +(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/is",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n="Vinsamlegast styttið texta um "+t+" staf";return t<=1?n:n+"i"},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Vinsamlegast skrifið "+t+" staf";return t>1&&(n+="i"),n+=" í viðbót",n},loadingMore:function(){return"Sæki fleiri niðurstöður…"},maximumSelected:function(e){return"Þú getur aðeins valið "+e.maximum+" atriði"},noResults:function(){return"Ekkert fannst"},searching:function(){return"Leita…"}}}),{define:e.define,require:e.require}})(); \ No newline at end of file diff --git a/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/it.js b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/it.js new file mode 100755 index 000000000..7796b9f76 --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/it.js @@ -0,0 +1,3 @@ +/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ + +(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/it",[],function(){return{errorLoading:function(){return"I risultati non possono essere caricati."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Per favore cancella "+t+" caratter";return t!==1?n+="i":n+="e",n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Per favore inserisci "+t+" o più caratteri";return n},loadingMore:function(){return"Caricando più risultati…"},maximumSelected:function(e){var t="Puoi selezionare solo "+e.maximum+" element";return e.maximum!==1?t+="i":t+="o",t},noResults:function(){return"Nessun risultato trovato"},searching:function(){return"Sto cercando…"}}}),{define:e.define,require:e.require}})(); \ No newline at end of file diff --git a/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/ja.js b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/ja.js new file mode 100755 index 000000000..9f4fff6cb --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/ja.js @@ -0,0 +1,3 @@ +/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ + +(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/ja",[],function(){return{errorLoading:function(){return"結果が読み込まれませんでした"},inputTooLong:function(e){var t=e.input.length-e.maximum,n=t+" 文字を削除してください";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="少なくとも "+t+" 文字を入力してください";return n},loadingMore:function(){return"読み込み中…"},maximumSelected:function(e){var t=e.maximum+" 件しか選択できません";return t},noResults:function(){return"対象が見つかりません"},searching:function(){return"検索しています…"}}}),{define:e.define,require:e.require}})(); \ No newline at end of file diff --git a/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/km.js b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/km.js new file mode 100755 index 000000000..8e94adcf3 --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/km.js @@ -0,0 +1,3 @@ +/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ + +(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/km",[],function(){return{errorLoading:function(){return"មិនអាចទាញយកទិន្នន័យ"},inputTooLong:function(e){var t=e.input.length-e.maximum,n="សូមលុបចេញ "+t+" អក្សរ";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="សូមបញ្ចូល"+t+" អក្សរ រឺ ច្រើនជាងនេះ";return n},loadingMore:function(){return"កំពុងទាញយកទិន្នន័យបន្ថែម..."},maximumSelected:function(e){var t="អ្នកអាចជ្រើសរើសបានតែ "+e.maximum+" ជម្រើសប៉ុណ្ណោះ";return t},noResults:function(){return"មិនមានលទ្ធផល"},searching:function(){return"កំពុងស្វែងរក..."}}}),{define:e.define,require:e.require}})(); \ No newline at end of file diff --git a/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/ko.js b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/ko.js new file mode 100755 index 000000000..4ed03215f --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/ko.js @@ -0,0 +1,3 @@ +/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ + +(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/ko",[],function(){return{errorLoading:function(){return"결과를 불러올 수 없습니다."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="너무 깁니다. "+t+" 글자 지워주세요.";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="너무 짧습니다. "+t+" 글자 더 입력해주세요.";return n},loadingMore:function(){return"불러오는 중…"},maximumSelected:function(e){var t="최대 "+e.maximum+"개까지만 선택 가능합니다.";return t},noResults:function(){return"결과가 없습니다."},searching:function(){return"검색 중…"}}}),{define:e.define,require:e.require}})(); \ No newline at end of file diff --git a/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/lt.js b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/lt.js new file mode 100755 index 000000000..05f3a6e5e --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/lt.js @@ -0,0 +1,3 @@ +/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ + +(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/lt",[],function(){function e(e,t,n,r){return e%10===1&&(e%100<11||e%100>19)?t:e%10>=2&&e%10<=9&&(e%100<11||e%100>19)?n:r}return{inputTooLong:function(t){var n=t.input.length-t.maximum,r="Pašalinkite "+n+" simbol";return r+=e(n,"į","ius","ių"),r},inputTooShort:function(t){var n=t.minimum-t.input.length,r="Įrašykite dar "+n+" simbol";return r+=e(n,"į","ius","ių"),r},loadingMore:function(){return"Kraunama daugiau rezultatų…"},maximumSelected:function(t){var n="Jūs galite pasirinkti tik "+t.maximum+" element";return n+=e(t.maximum,"ą","us","ų"),n},noResults:function(){return"Atitikmenų nerasta"},searching:function(){return"Ieškoma…"}}}),{define:e.define,require:e.require}})(); \ No newline at end of file diff --git a/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/lv.js b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/lv.js new file mode 100755 index 000000000..df8ee9423 --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/lv.js @@ -0,0 +1,3 @@ +/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ + +(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/lv",[],function(){function e(e,t,n,r){return e===11?t:e%10===1?n:r}return{inputTooLong:function(t){var n=t.input.length-t.maximum,r="Lūdzu ievadiet par "+n;return r+=" simbol"+e(n,"iem","u","iem"),r+" mazāk"},inputTooShort:function(t){var n=t.minimum-t.input.length,r="Lūdzu ievadiet vēl "+n;return r+=" simbol"+e(n,"us","u","us"),r},loadingMore:function(){return"Datu ielāde…"},maximumSelected:function(t){var n="Jūs varat izvēlēties ne vairāk kā "+t.maximum;return n+=" element"+e(t.maximum,"us","u","us"),n},noResults:function(){return"Sakritību nav"},searching:function(){return"Meklēšana…"}}}),{define:e.define,require:e.require}})(); \ No newline at end of file diff --git a/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/mk.js b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/mk.js new file mode 100755 index 000000000..319ecca14 --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/mk.js @@ -0,0 +1,3 @@ +/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ + +(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/mk",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n="Ве молиме внесете "+e.maximum+" помалку карактер";return e.maximum!==1&&(n+="и"),n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Ве молиме внесете уште "+e.maximum+" карактер";return e.maximum!==1&&(n+="и"),n},loadingMore:function(){return"Вчитување резултати…"},maximumSelected:function(e){var t="Можете да изберете само "+e.maximum+" ставк";return e.maximum===1?t+="а":t+="и",t},noResults:function(){return"Нема пронајдено совпаѓања"},searching:function(){return"Пребарување…"}}}),{define:e.define,require:e.require}})(); \ No newline at end of file diff --git a/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/ms.js b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/ms.js new file mode 100755 index 000000000..4258f125b --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/ms.js @@ -0,0 +1,3 @@ +/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ + +(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/ms",[],function(){return{errorLoading:function(){return"Keputusan tidak berjaya dimuatkan."},inputTooLong:function(e){var t=e.input.length-e.maximum;return"Sila hapuskan "+t+" aksara"},inputTooShort:function(e){var t=e.minimum-e.input.length;return"Sila masukkan "+t+" atau lebih aksara"},loadingMore:function(){return"Sedang memuatkan keputusan…"},maximumSelected:function(e){return"Anda hanya boleh memilih "+e.maximum+" pilihan"},noResults:function(){return"Tiada padanan yang ditemui"},searching:function(){return"Mencari…"}}}),{define:e.define,require:e.require}})(); \ No newline at end of file diff --git a/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/nb.js b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/nb.js new file mode 100755 index 000000000..6770087ce --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/nb.js @@ -0,0 +1,3 @@ +/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ + +(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/nb",[],function(){return{errorLoading:function(){return"Kunne ikke hente resultater."},inputTooLong:function(e){var t=e.input.length-e.maximum;return"Vennligst fjern "+t+" tegn"},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Vennligst skriv inn ";return t>1?n+=" flere tegn":n+=" tegn til",n},loadingMore:function(){return"Laster flere resultater…"},maximumSelected:function(e){return"Du kan velge maks "+e.maximum+" elementer"},noResults:function(){return"Ingen treff"},searching:function(){return"Søker…"}}}),{define:e.define,require:e.require}})(); \ No newline at end of file diff --git a/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/nl.js b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/nl.js new file mode 100755 index 000000000..8bd5e3cf4 --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/nl.js @@ -0,0 +1,3 @@ +/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ + +(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/nl",[],function(){return{errorLoading:function(){return"De resultaten konden niet worden geladen."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Gelieve "+t+" karakters te verwijderen";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Gelieve "+t+" of meer karakters in te voeren";return n},loadingMore:function(){return"Meer resultaten laden…"},maximumSelected:function(e){var t=e.maximum==1?"kan":"kunnen",n="Er "+t+" maar "+e.maximum+" item";return e.maximum!=1&&(n+="s"),n+=" worden geselecteerd",n},noResults:function(){return"Geen resultaten gevonden…"},searching:function(){return"Zoeken…"}}}),{define:e.define,require:e.require}})(); \ No newline at end of file diff --git a/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/pl.js b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/pl.js new file mode 100755 index 000000000..54ba28e9b --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/pl.js @@ -0,0 +1,3 @@ +/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ + +(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/pl",[],function(){var e=["znak","znaki","znaków"],t=["element","elementy","elementów"],n=function(t,n){if(t===1)return n[0];if(t>1&&t<=4)return n[1];if(t>=5)return n[2]};return{errorLoading:function(){return"Nie można załadować wyników."},inputTooLong:function(t){var r=t.input.length-t.maximum;return"Usuń "+r+" "+n(r,e)},inputTooShort:function(t){var r=t.minimum-t.input.length;return"Podaj przynajmniej "+r+" "+n(r,e)},loadingMore:function(){return"Trwa ładowanie…"},maximumSelected:function(e){return"Możesz zaznaczyć tylko "+e.maximum+" "+n(e.maximum,t)},noResults:function(){return"Brak wyników"},searching:function(){return"Trwa wyszukiwanie…"}}}),{define:e.define,require:e.require}})(); \ No newline at end of file diff --git a/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/pt-BR.js b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/pt-BR.js new file mode 100755 index 000000000..a6629c8ae --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/pt-BR.js @@ -0,0 +1,3 @@ +/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ + +(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/pt-BR",[],function(){return{errorLoading:function(){return"Os resultados não puderam ser carregados."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Apague "+t+" caracter";return t!=1&&(n+="es"),n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Digite "+t+" ou mais caracteres";return n},loadingMore:function(){return"Carregando mais resultados…"},maximumSelected:function(e){var t="Você só pode selecionar "+e.maximum+" ite";return e.maximum==1?t+="m":t+="ns",t},noResults:function(){return"Nenhum resultado encontrado"},searching:function(){return"Buscando…"}}}),{define:e.define,require:e.require}})(); \ No newline at end of file diff --git a/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/pt.js b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/pt.js new file mode 100755 index 000000000..0cbda561b --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/pt.js @@ -0,0 +1,3 @@ +/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ + +(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/pt",[],function(){return{errorLoading:function(){return"Os resultados não puderam ser carregados."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Por favor apague "+t+" ";return n+=t!=1?"caracteres":"carácter",n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Introduza "+t+" ou mais caracteres";return n},loadingMore:function(){return"A carregar mais resultados…"},maximumSelected:function(e){var t="Apenas pode seleccionar "+e.maximum+" ";return t+=e.maximum!=1?"itens":"item",t},noResults:function(){return"Sem resultados"},searching:function(){return"A procurar…"}}}),{define:e.define,require:e.require}})(); \ No newline at end of file diff --git a/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/ro.js b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/ro.js new file mode 100755 index 000000000..788a26376 --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/ro.js @@ -0,0 +1,3 @@ +/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ + +(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/ro",[],function(){return{errorLoading:function(){return"Rezultatele nu au putut fi incărcate."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Vă rugăm să ștergeți"+t+" caracter";return t!==1&&(n+="e"),n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Vă rugăm să introduceți "+t+"sau mai multe caractere";return n},loadingMore:function(){return"Se încarcă mai multe rezultate…"},maximumSelected:function(e){var t="Aveți voie să selectați cel mult "+e.maximum;return t+=" element",e.maximum!==1&&(t+="e"),t},noResults:function(){return"Nu au fost găsite rezultate"},searching:function(){return"Căutare…"}}}),{define:e.define,require:e.require}})(); \ No newline at end of file diff --git a/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/ru.js b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/ru.js new file mode 100755 index 000000000..9ecab8091 --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/ru.js @@ -0,0 +1,3 @@ +/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ + +(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/ru",[],function(){function e(e,t,n,r){return e%10<5&&e%10>0&&e%100<5||e%100>20?e%10>1?n:t:r}return{errorLoading:function(){return"Невозможно загрузить результаты"},inputTooLong:function(t){var n=t.input.length-t.maximum,r="Пожалуйста, введите на "+n+" символ";return r+=e(n,"","a","ов"),r+=" меньше",r},inputTooShort:function(t){var n=t.minimum-t.input.length,r="Пожалуйста, введите еще хотя бы "+n+" символ";return r+=e(n,"","a","ов"),r},loadingMore:function(){return"Загрузка данных…"},maximumSelected:function(t){var n="Вы можете выбрать не более "+t.maximum+" элемент";return n+=e(t.maximum,"","a","ов"),n},noResults:function(){return"Совпадений не найдено"},searching:function(){return"Поиск…"}}}),{define:e.define,require:e.require}})(); \ No newline at end of file diff --git a/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/sk.js b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/sk.js new file mode 100755 index 000000000..82f294138 --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/sk.js @@ -0,0 +1,3 @@ +/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ + +(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/sk",[],function(){var e={2:function(e){return e?"dva":"dve"},3:function(){return"tri"},4:function(){return"štyri"}};return{inputTooLong:function(t){var n=t.input.length-t.maximum;return n==1?"Prosím, zadajte o jeden znak menej":n>=2&&n<=4?"Prosím, zadajte o "+e[n](!0)+" znaky menej":"Prosím, zadajte o "+n+" znakov menej"},inputTooShort:function(t){var n=t.minimum-t.input.length;return n==1?"Prosím, zadajte ešte jeden znak":n<=4?"Prosím, zadajte ešte ďalšie "+e[n](!0)+" znaky":"Prosím, zadajte ešte ďalších "+n+" znakov"},loadingMore:function(){return"Loading more results…"},maximumSelected:function(t){return t.maximum==1?"Môžete zvoliť len jednu položku":t.maximum>=2&&t.maximum<=4?"Môžete zvoliť najviac "+e[t.maximum](!1)+" položky":"Môžete zvoliť najviac "+t.maximum+" položiek"},noResults:function(){return"Nenašli sa žiadne položky"},searching:function(){return"Vyhľadávanie…"}}}),{define:e.define,require:e.require}})(); \ No newline at end of file diff --git a/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/sr-Cyrl.js b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/sr-Cyrl.js new file mode 100755 index 000000000..e9453940c --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/sr-Cyrl.js @@ -0,0 +1,3 @@ +/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ + +(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/sr-Cyrl",[],function(){function e(e,t,n,r){return e%10==1&&e%100!=11?t:e%10>=2&&e%10<=4&&(e%100<12||e%100>14)?n:r}return{errorLoading:function(){return"Преузимање није успело."},inputTooLong:function(t){var n=t.input.length-t.maximum,r="Обришите "+n+" симбол";return r+=e(n,"","а","а"),r},inputTooShort:function(t){var n=t.minimum-t.input.length,r="Укуцајте бар још "+n+" симбол";return r+=e(n,"","а","а"),r},loadingMore:function(){return"Преузимање још резултата…"},maximumSelected:function(t){var n="Можете изабрати само "+t.maximum+" ставк";return n+=e(t.maximum,"у","е","и"),n},noResults:function(){return"Ништа није пронађено"},searching:function(){return"Претрага…"}}}),{define:e.define,require:e.require}})(); \ No newline at end of file diff --git a/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/sr.js b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/sr.js new file mode 100755 index 000000000..ac0cc721f --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/sr.js @@ -0,0 +1,3 @@ +/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ + +(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/sr",[],function(){function e(e,t,n,r){return e%10==1&&e%100!=11?t:e%10>=2&&e%10<=4&&(e%100<12||e%100>14)?n:r}return{errorLoading:function(){return"Preuzimanje nije uspelo."},inputTooLong:function(t){var n=t.input.length-t.maximum,r="Obrišite "+n+" simbol";return r+=e(n,"","a","a"),r},inputTooShort:function(t){var n=t.minimum-t.input.length,r="Ukucajte bar još "+n+" simbol";return r+=e(n,"","a","a"),r},loadingMore:function(){return"Preuzimanje još rezultata…"},maximumSelected:function(t){var n="Možete izabrati samo "+t.maximum+" stavk";return n+=e(t.maximum,"u","e","i"),n},noResults:function(){return"Ništa nije pronađeno"},searching:function(){return"Pretraga…"}}}),{define:e.define,require:e.require}})(); \ No newline at end of file diff --git a/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/sv.js b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/sv.js new file mode 100755 index 000000000..bedac08c4 --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/sv.js @@ -0,0 +1,3 @@ +/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ + +(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/sv",[],function(){return{errorLoading:function(){return"Resultat kunde inte laddas."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Vänligen sudda ut "+t+" tecken";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Vänligen skriv in "+t+" eller fler tecken";return n},loadingMore:function(){return"Laddar fler resultat…"},maximumSelected:function(e){var t="Du kan max välja "+e.maximum+" element";return t},noResults:function(){return"Inga träffar"},searching:function(){return"Söker…"}}}),{define:e.define,require:e.require}})(); \ No newline at end of file diff --git a/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/th.js b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/th.js new file mode 100755 index 000000000..097a86c69 --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/th.js @@ -0,0 +1,3 @@ +/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ + +(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/th",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n="โปรดลบออก "+t+" ตัวอักษร";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="โปรดพิมพ์เพิ่มอีก "+t+" ตัวอักษร";return n},loadingMore:function(){return"กำลังค้นข้อมูลเพิ่ม…"},maximumSelected:function(e){var t="คุณสามารถเลือกได้ไม่เกิน "+e.maximum+" รายการ";return t},noResults:function(){return"ไม่พบข้อมูล"},searching:function(){return"กำลังค้นข้อมูล…"}}}),{define:e.define,require:e.require}})(); \ No newline at end of file diff --git a/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/tr.js b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/tr.js new file mode 100755 index 000000000..25d27a877 --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/tr.js @@ -0,0 +1,3 @@ +/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ + +(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/tr",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n=t+" karakter daha girmelisiniz";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="En az "+t+" karakter daha girmelisiniz";return n},loadingMore:function(){return"Daha fazla…"},maximumSelected:function(e){var t="Sadece "+e.maximum+" seçim yapabilirsiniz";return t},noResults:function(){return"Sonuç bulunamadı"},searching:function(){return"Aranıyor…"}}}),{define:e.define,require:e.require}})(); \ No newline at end of file diff --git a/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/uk.js b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/uk.js new file mode 100755 index 000000000..eb3ca8903 --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/uk.js @@ -0,0 +1,3 @@ +/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ + +(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/uk",[],function(){function e(e,t,n,r){return e%100>10&&e%100<15?r:e%10===1?t:e%10>1&&e%10<5?n:r}return{errorLoading:function(){return"Неможливо завантажити результати"},inputTooLong:function(t){var n=t.input.length-t.maximum;return"Будь ласка, видаліть "+n+" "+e(t.maximum,"літеру","літери","літер")},inputTooShort:function(e){var t=e.minimum-e.input.length;return"Будь ласка, введіть "+t+" або більше літер"},loadingMore:function(){return"Завантаження інших результатів…"},maximumSelected:function(t){return"Ви можете вибрати лише "+t.maximum+" "+e(t.maximum,"пункт","пункти","пунктів")},noResults:function(){return"Нічого не знайдено"},searching:function(){return"Пошук…"}}}),{define:e.define,require:e.require}})(); \ No newline at end of file diff --git a/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/vi.js b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/vi.js new file mode 100755 index 000000000..8975b8ac6 --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/vi.js @@ -0,0 +1,3 @@ +/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ + +(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/vi",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n="Vui lòng nhập ít hơn "+t+" ký tự";return t!=1&&(n+="s"),n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Vui lòng nhập nhiều hơn "+t+' ký tự"';return n},loadingMore:function(){return"Đang lấy thêm kết quả…"},maximumSelected:function(e){var t="Chỉ có thể chọn được "+e.maximum+" lựa chọn";return t},noResults:function(){return"Không tìm thấy kết quả"},searching:function(){return"Đang tìm…"}}}),{define:e.define,require:e.require}})(); \ No newline at end of file diff --git a/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/zh-CN.js b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/zh-CN.js new file mode 100755 index 000000000..2ed959723 --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/zh-CN.js @@ -0,0 +1,3 @@ +/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ + +(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/zh-CN",[],function(){return{errorLoading:function(){return"无法载入结果。"},inputTooLong:function(e){var t=e.input.length-e.maximum,n="请删除"+t+"个字符";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="请再输入至少"+t+"个字符";return n},loadingMore:function(){return"载入更多结果…"},maximumSelected:function(e){var t="最多只能选择"+e.maximum+"个项目";return t},noResults:function(){return"未找到结果"},searching:function(){return"搜索中…"}}}),{define:e.define,require:e.require}})(); \ No newline at end of file diff --git a/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/zh-TW.js b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/zh-TW.js new file mode 100755 index 000000000..ea0812ee0 --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/vendor/select2/i18n/zh-TW.js @@ -0,0 +1,3 @@ +/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */ + +(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/zh-TW",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n="請刪掉"+t+"個字元";return n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="請再輸入"+t+"個字元";return n},loadingMore:function(){return"載入中…"},maximumSelected:function(e){var t="你只能選擇最多"+e.maximum+"項";return t},noResults:function(){return"沒有找到相符的項目"},searching:function(){return"搜尋中…"}}}),{define:e.define,require:e.require}})(); \ No newline at end of file diff --git a/src/dal_legacy_static/static/admin/js/vendor/select2/select2.full.js b/src/dal_legacy_static/static/admin/js/vendor/select2/select2.full.js new file mode 100755 index 000000000..e750834ef --- /dev/null +++ b/src/dal_legacy_static/static/admin/js/vendor/select2/select2.full.js @@ -0,0 +1,6436 @@ +/*! + * Select2 4.0.3 + * https://select2.github.io + * + * Released under the MIT license + * https://github.com/select2/select2/blob/master/LICENSE.md + */ +(function (factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define(['jquery'], factory); + } else if (typeof exports === 'object') { + // Node/CommonJS + factory(require('jquery')); + } else { + // Browser globals + factory(jQuery); + } +}(function (jQuery) { + // This is needed so we can catch the AMD loader configuration and use it + // The inner file should be wrapped (by `banner.start.js`) in a function that + // returns the AMD loader references. + var S2 = +(function () { + // Restore the Select2 AMD loader so it can be used + // Needed mostly in the language files, where the loader is not inserted + if (jQuery && jQuery.fn && jQuery.fn.select2 && jQuery.fn.select2.amd) { + var S2 = jQuery.fn.select2.amd; + } +var S2;(function () { if (!S2 || !S2.requirejs) { +if (!S2) { S2 = {}; } else { require = S2; } +/** + * @license almond 0.3.1 Copyright (c) 2011-2014, The Dojo Foundation All Rights Reserved. + * Available via the MIT or new BSD license. + * see: http://github.com/jrburke/almond for details + */ +//Going sloppy to avoid 'use strict' string cost, but strict practices should +//be followed. +/*jslint sloppy: true */ +/*global setTimeout: false */ + +var requirejs, require, define; +(function (undef) { + var main, req, makeMap, handlers, + defined = {}, + waiting = {}, + config = {}, + defining = {}, + hasOwn = Object.prototype.hasOwnProperty, + aps = [].slice, + jsSuffixRegExp = /\.js$/; + + function hasProp(obj, prop) { + return hasOwn.call(obj, prop); + } + + /** + * Given a relative module name, like ./something, normalize it to + * a real name that can be mapped to a path. + * @param {String} name the relative name + * @param {String} baseName a real name that the name arg is relative + * to. + * @returns {String} normalized name + */ + function normalize(name, baseName) { + var nameParts, nameSegment, mapValue, foundMap, lastIndex, + foundI, foundStarMap, starI, i, j, part, + baseParts = baseName && baseName.split("/"), + map = config.map, + starMap = (map && map['*']) || {}; + + //Adjust any relative paths. + if (name && name.charAt(0) === ".") { + //If have a base name, try to normalize against it, + //otherwise, assume it is a top-level require that will + //be relative to baseUrl in the end. + if (baseName) { + name = name.split('/'); + lastIndex = name.length - 1; + + // Node .js allowance: + if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) { + name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, ''); + } + + //Lop off the last part of baseParts, so that . matches the + //"directory" and not name of the baseName's module. For instance, + //baseName of "one/two/three", maps to "one/two/three.js", but we + //want the directory, "one/two" for this normalization. + name = baseParts.slice(0, baseParts.length - 1).concat(name); + + //start trimDots + for (i = 0; i < name.length; i += 1) { + part = name[i]; + if (part === ".") { + name.splice(i, 1); + i -= 1; + } else if (part === "..") { + if (i === 1 && (name[2] === '..' || name[0] === '..')) { + //End of the line. Keep at least one non-dot + //path segment at the front so it can be mapped + //correctly to disk. Otherwise, there is likely + //no path mapping for a path starting with '..'. + //This can still fail, but catches the most reasonable + //uses of .. + break; + } else if (i > 0) { + name.splice(i - 1, 2); + i -= 2; + } + } + } + //end trimDots + + name = name.join("/"); + } else if (name.indexOf('./') === 0) { + // No baseName, so this is ID is resolved relative + // to baseUrl, pull off the leading dot. + name = name.substring(2); + } + } + + //Apply map config if available. + if ((baseParts || starMap) && map) { + nameParts = name.split('/'); + + for (i = nameParts.length; i > 0; i -= 1) { + nameSegment = nameParts.slice(0, i).join("/"); + + if (baseParts) { + //Find the longest baseName segment match in the config. + //So, do joins on the biggest to smallest lengths of baseParts. + for (j = baseParts.length; j > 0; j -= 1) { + mapValue = map[baseParts.slice(0, j).join('/')]; + + //baseName segment has config, find if it has one for + //this name. + if (mapValue) { + mapValue = mapValue[nameSegment]; + if (mapValue) { + //Match, update name to the new value. + foundMap = mapValue; + foundI = i; + break; + } + } + } + } + + if (foundMap) { + break; + } + + //Check for a star map match, but just hold on to it, + //if there is a shorter segment match later in a matching + //config, then favor over this star map. + if (!foundStarMap && starMap && starMap[nameSegment]) { + foundStarMap = starMap[nameSegment]; + starI = i; + } + } + + if (!foundMap && foundStarMap) { + foundMap = foundStarMap; + foundI = starI; + } + + if (foundMap) { + nameParts.splice(0, foundI, foundMap); + name = nameParts.join('/'); + } + } + + return name; + } + + function makeRequire(relName, forceSync) { + return function () { + //A version of a require function that passes a moduleName + //value for items that may need to + //look up paths relative to the moduleName + var args = aps.call(arguments, 0); + + //If first arg is not require('string'), and there is only + //one arg, it is the array form without a callback. Insert + //a null so that the following concat is correct. + if (typeof args[0] !== 'string' && args.length === 1) { + args.push(null); + } + return req.apply(undef, args.concat([relName, forceSync])); + }; + } + + function makeNormalize(relName) { + return function (name) { + return normalize(name, relName); + }; + } + + function makeLoad(depName) { + return function (value) { + defined[depName] = value; + }; + } + + function callDep(name) { + if (hasProp(waiting, name)) { + var args = waiting[name]; + delete waiting[name]; + defining[name] = true; + main.apply(undef, args); + } + + if (!hasProp(defined, name) && !hasProp(defining, name)) { + throw new Error('No ' + name); + } + return defined[name]; + } + + //Turns a plugin!resource to [plugin, resource] + //with the plugin being undefined if the name + //did not have a plugin prefix. + function splitPrefix(name) { + var prefix, + index = name ? name.indexOf('!') : -1; + if (index > -1) { + prefix = name.substring(0, index); + name = name.substring(index + 1, name.length); + } + return [prefix, name]; + } + + /** + * Makes a name map, normalizing the name, and using a plugin + * for normalization if necessary. Grabs a ref to plugin + * too, as an optimization. + */ + makeMap = function (name, relName) { + var plugin, + parts = splitPrefix(name), + prefix = parts[0]; + + name = parts[1]; + + if (prefix) { + prefix = normalize(prefix, relName); + plugin = callDep(prefix); + } + + //Normalize according + if (prefix) { + if (plugin && plugin.normalize) { + name = plugin.normalize(name, makeNormalize(relName)); + } else { + name = normalize(name, relName); + } + } else { + name = normalize(name, relName); + parts = splitPrefix(name); + prefix = parts[0]; + name = parts[1]; + if (prefix) { + plugin = callDep(prefix); + } + } + + //Using ridiculous property names for space reasons + return { + f: prefix ? prefix + '!' + name : name, //fullName + n: name, + pr: prefix, + p: plugin + }; + }; + + function makeConfig(name) { + return function () { + return (config && config.config && config.config[name]) || {}; + }; + } + + handlers = { + require: function (name) { + return makeRequire(name); + }, + exports: function (name) { + var e = defined[name]; + if (typeof e !== 'undefined') { + return e; + } else { + return (defined[name] = {}); + } + }, + module: function (name) { + return { + id: name, + uri: '', + exports: defined[name], + config: makeConfig(name) + }; + } + }; + + main = function (name, deps, callback, relName) { + var cjsModule, depName, ret, map, i, + args = [], + callbackType = typeof callback, + usingExports; + + //Use name if no relName + relName = relName || name; + + //Call the callback to define the module, if necessary. + if (callbackType === 'undefined' || callbackType === 'function') { + //Pull out the defined dependencies and pass the ordered + //values to the callback. + //Default to [require, exports, module] if no deps + deps = !deps.length && callback.length ? ['require', 'exports', 'module'] : deps; + for (i = 0; i < deps.length; i += 1) { + map = makeMap(deps[i], relName); + depName = map.f; + + //Fast path CommonJS standard dependencies. + if (depName === "require") { + args[i] = handlers.require(name); + } else if (depName === "exports") { + //CommonJS module spec 1.1 + args[i] = handlers.exports(name); + usingExports = true; + } else if (depName === "module") { + //CommonJS module spec 1.1 + cjsModule = args[i] = handlers.module(name); + } else if (hasProp(defined, depName) || + hasProp(waiting, depName) || + hasProp(defining, depName)) { + args[i] = callDep(depName); + } else if (map.p) { + map.p.load(map.n, makeRequire(relName, true), makeLoad(depName), {}); + args[i] = defined[depName]; + } else { + throw new Error(name + ' missing ' + depName); + } + } + + ret = callback ? callback.apply(defined[name], args) : undefined; + + if (name) { + //If setting exports via "module" is in play, + //favor that over return value and exports. After that, + //favor a non-undefined return value over exports use. + if (cjsModule && cjsModule.exports !== undef && + cjsModule.exports !== defined[name]) { + defined[name] = cjsModule.exports; + } else if (ret !== undef || !usingExports) { + //Use the return value from the function. + defined[name] = ret; + } + } + } else if (name) { + //May just be an object definition for the module. Only + //worry about defining if have a module name. + defined[name] = callback; + } + }; + + requirejs = require = req = function (deps, callback, relName, forceSync, alt) { + if (typeof deps === "string") { + if (handlers[deps]) { + //callback in this case is really relName + return handlers[deps](callback); + } + //Just return the module wanted. In this scenario, the + //deps arg is the module name, and second arg (if passed) + //is just the relName. + //Normalize module name, if it contains . or .. + return callDep(makeMap(deps, callback).f); + } else if (!deps.splice) { + //deps is a config object, not an array. + config = deps; + if (config.deps) { + req(config.deps, config.callback); + } + if (!callback) { + return; + } + + if (callback.splice) { + //callback is an array, which means it is a dependency list. + //Adjust args if there are dependencies + deps = callback; + callback = relName; + relName = null; + } else { + deps = undef; + } + } + + //Support require(['a']) + callback = callback || function () {}; + + //If relName is a function, it is an errback handler, + //so remove it. + if (typeof relName === 'function') { + relName = forceSync; + forceSync = alt; + } + + //Simulate async callback; + if (forceSync) { + main(undef, deps, callback, relName); + } else { + //Using a non-zero value because of concern for what old browsers + //do, and latest browsers "upgrade" to 4 if lower value is used: + //http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html#dom-windowtimers-settimeout: + //If want a value immediately, use require('id') instead -- something + //that works in almond on the global level, but not guaranteed and + //unlikely to work in other AMD implementations. + setTimeout(function () { + main(undef, deps, callback, relName); + }, 4); + } + + return req; + }; + + /** + * Just drops the config on the floor, but returns req in case + * the config return value is used. + */ + req.config = function (cfg) { + return req(cfg); + }; + + /** + * Expose module registry for debugging and tooling + */ + requirejs._defined = defined; + + define = function (name, deps, callback) { + if (typeof name !== 'string') { + throw new Error('See almond README: incorrect module build, no module name'); + } + + //This module may not have dependencies + if (!deps.splice) { + //deps is not an array, so probably means + //an object literal or factory function for + //the value. Adjust args. + callback = deps; + deps = []; + } + + if (!hasProp(defined, name) && !hasProp(waiting, name)) { + waiting[name] = [name, deps, callback]; + } + }; + + define.amd = { + jQuery: true + }; +}()); + +S2.requirejs = requirejs;S2.require = require;S2.define = define; +} +}()); +S2.define("almond", function(){}); + +/* global jQuery:false, $:false */ +S2.define('jquery',[],function () { + var _$ = jQuery || $; + + if (_$ == null && console && console.error) { + console.error( + 'Select2: An instance of jQuery or a jQuery-compatible library was not ' + + 'found. Make sure that you are including jQuery before Select2 on your ' + + 'web page.' + ); + } + + return _$; +}); + +S2.define('select2/utils',[ + 'jquery' +], function ($) { + var Utils = {}; + + Utils.Extend = function (ChildClass, SuperClass) { + var __hasProp = {}.hasOwnProperty; + + function BaseConstructor () { + this.constructor = ChildClass; + } + + for (var key in SuperClass) { + if (__hasProp.call(SuperClass, key)) { + ChildClass[key] = SuperClass[key]; + } + } + + BaseConstructor.prototype = SuperClass.prototype; + ChildClass.prototype = new BaseConstructor(); + ChildClass.__super__ = SuperClass.prototype; + + return ChildClass; + }; + + function getMethods (theClass) { + var proto = theClass.prototype; + + var methods = []; + + for (var methodName in proto) { + var m = proto[methodName]; + + if (typeof m !== 'function') { + continue; + } + + if (methodName === 'constructor') { + continue; + } + + methods.push(methodName); + } + + return methods; + } + + Utils.Decorate = function (SuperClass, DecoratorClass) { + var decoratedMethods = getMethods(DecoratorClass); + var superMethods = getMethods(SuperClass); + + function DecoratedClass () { + var unshift = Array.prototype.unshift; + + var argCount = DecoratorClass.prototype.constructor.length; + + var calledConstructor = SuperClass.prototype.constructor; + + if (argCount > 0) { + unshift.call(arguments, SuperClass.prototype.constructor); + + calledConstructor = DecoratorClass.prototype.constructor; + } + + calledConstructor.apply(this, arguments); + } + + DecoratorClass.displayName = SuperClass.displayName; + + function ctr () { + this.constructor = DecoratedClass; + } + + DecoratedClass.prototype = new ctr(); + + for (var m = 0; m < superMethods.length; m++) { + var superMethod = superMethods[m]; + + DecoratedClass.prototype[superMethod] = + SuperClass.prototype[superMethod]; + } + + var calledMethod = function (methodName) { + // Stub out the original method if it's not decorating an actual method + var originalMethod = function () {}; + + if (methodName in DecoratedClass.prototype) { + originalMethod = DecoratedClass.prototype[methodName]; + } + + var decoratedMethod = DecoratorClass.prototype[methodName]; + + return function () { + var unshift = Array.prototype.unshift; + + unshift.call(arguments, originalMethod); + + return decoratedMethod.apply(this, arguments); + }; + }; + + for (var d = 0; d < decoratedMethods.length; d++) { + var decoratedMethod = decoratedMethods[d]; + + DecoratedClass.prototype[decoratedMethod] = calledMethod(decoratedMethod); + } + + return DecoratedClass; + }; + + var Observable = function () { + this.listeners = {}; + }; + + Observable.prototype.on = function (event, callback) { + this.listeners = this.listeners || {}; + + if (event in this.listeners) { + this.listeners[event].push(callback); + } else { + this.listeners[event] = [callback]; + } + }; + + Observable.prototype.trigger = function (event) { + var slice = Array.prototype.slice; + var params = slice.call(arguments, 1); + + this.listeners = this.listeners || {}; + + // Params should always come in as an array + if (params == null) { + params = []; + } + + // If there are no arguments to the event, use a temporary object + if (params.length === 0) { + params.push({}); + } + + // Set the `_type` of the first object to the event + params[0]._type = event; + + if (event in this.listeners) { + this.invoke(this.listeners[event], slice.call(arguments, 1)); + } + + if ('*' in this.listeners) { + this.invoke(this.listeners['*'], arguments); + } + }; + + Observable.prototype.invoke = function (listeners, params) { + for (var i = 0, len = listeners.length; i < len; i++) { + listeners[i].apply(this, params); + } + }; + + Utils.Observable = Observable; + + Utils.generateChars = function (length) { + var chars = ''; + + for (var i = 0; i < length; i++) { + var randomChar = Math.floor(Math.random() * 36); + chars += randomChar.toString(36); + } + + return chars; + }; + + Utils.bind = function (func, context) { + return function () { + func.apply(context, arguments); + }; + }; + + Utils._convertData = function (data) { + for (var originalKey in data) { + var keys = originalKey.split('-'); + + var dataLevel = data; + + if (keys.length === 1) { + continue; + } + + for (var k = 0; k < keys.length; k++) { + var key = keys[k]; + + // Lowercase the first letter + // By default, dash-separated becomes camelCase + key = key.substring(0, 1).toLowerCase() + key.substring(1); + + if (!(key in dataLevel)) { + dataLevel[key] = {}; + } + + if (k == keys.length - 1) { + dataLevel[key] = data[originalKey]; + } + + dataLevel = dataLevel[key]; + } + + delete data[originalKey]; + } + + return data; + }; + + Utils.hasScroll = function (index, el) { + // Adapted from the function created by @ShadowScripter + // and adapted by @BillBarry on the Stack Exchange Code Review website. + // The original code can be found at + // http://codereview.stackexchange.com/q/13338 + // and was designed to be used with the Sizzle selector engine. + + var $el = $(el); + var overflowX = el.style.overflowX; + var overflowY = el.style.overflowY; + + //Check both x and y declarations + if (overflowX === overflowY && + (overflowY === 'hidden' || overflowY === 'visible')) { + return false; + } + + if (overflowX === 'scroll' || overflowY === 'scroll') { + return true; + } + + return ($el.innerHeight() < el.scrollHeight || + $el.innerWidth() < el.scrollWidth); + }; + + Utils.escapeMarkup = function (markup) { + var replaceMap = { + '\\': '\', + '&': '&', + '<': '<', + '>': '>', + '"': '"', + '\'': ''', + '/': '/' + }; + + // Do not try to escape the markup if it's not a string + if (typeof markup !== 'string') { + return markup; + } + + return String(markup).replace(/[&<>"'\/\\]/g, function (match) { + return replaceMap[match]; + }); + }; + + // Append an array of jQuery nodes to a given element. + Utils.appendMany = function ($element, $nodes) { + // jQuery 1.7.x does not support $.fn.append() with an array + // Fall back to a jQuery object collection using $.fn.add() + if ($.fn.jquery.substr(0, 3) === '1.7') { + var $jqNodes = $(); + + $.map($nodes, function (node) { + $jqNodes = $jqNodes.add(node); + }); + + $nodes = $jqNodes; + } + + $element.append($nodes); + }; + + return Utils; +}); + +S2.define('select2/results',[ + 'jquery', + './utils' +], function ($, Utils) { + function Results ($element, options, dataAdapter) { + this.$element = $element; + this.data = dataAdapter; + this.options = options; + + Results.__super__.constructor.call(this); + } + + Utils.Extend(Results, Utils.Observable); + + Results.prototype.render = function () { + var $results = $( + '
    ' + ); + + if (this.options.get('multiple')) { + $results.attr('aria-multiselectable', 'true'); + } + + this.$results = $results; + + return $results; + }; + + Results.prototype.clear = function () { + this.$results.empty(); + }; + + Results.prototype.displayMessage = function (params) { + var escapeMarkup = this.options.get('escapeMarkup'); + + this.clear(); + this.hideLoading(); + + var $message = $( + '
  • ' + ); + + var message = this.options.get('translations').get(params.message); + + $message.append( + escapeMarkup( + message(params.args) + ) + ); + + $message[0].className += ' select2-results__message'; + + this.$results.append($message); + }; + + Results.prototype.hideMessages = function () { + this.$results.find('.select2-results__message').remove(); + }; + + Results.prototype.append = function (data) { + this.hideLoading(); + + var $options = []; + + if (data.results == null || data.results.length === 0) { + if (this.$results.children().length === 0) { + this.trigger('results:message', { + message: 'noResults' + }); + } + + return; + } + + data.results = this.sort(data.results); + + for (var d = 0; d < data.results.length; d++) { + var item = data.results[d]; + + var $option = this.option(item); + + $options.push($option); + } + + this.$results.append($options); + }; + + Results.prototype.position = function ($results, $dropdown) { + var $resultsContainer = $dropdown.find('.select2-results'); + $resultsContainer.append($results); + }; + + Results.prototype.sort = function (data) { + var sorter = this.options.get('sorter'); + + return sorter(data); + }; + + Results.prototype.highlightFirstItem = function () { + var $options = this.$results + .find('.select2-results__option[aria-selected]'); + + var $selected = $options.filter('[aria-selected=true]'); + + // Check if there are any selected options + if ($selected.length > 0) { + // If there are selected options, highlight the first + $selected.first().trigger('mouseenter'); + } else { + // If there are no selected options, highlight the first option + // in the dropdown + $options.first().trigger('mouseenter'); + } + + this.ensureHighlightVisible(); + }; + + Results.prototype.setClasses = function () { + var self = this; + + this.data.current(function (selected) { + var selectedIds = $.map(selected, function (s) { + return s.id.toString(); + }); + + var $options = self.$results + .find('.select2-results__option[aria-selected]'); + + $options.each(function () { + var $option = $(this); + + var item = $.data(this, 'data'); + + // id needs to be converted to a string when comparing + var id = '' + item.id; + + if ((item.element != null && item.element.selected) || + (item.element == null && $.inArray(id, selectedIds) > -1)) { + $option.attr('aria-selected', 'true'); + } else { + $option.attr('aria-selected', 'false'); + } + }); + + }); + }; + + Results.prototype.showLoading = function (params) { + this.hideLoading(); + + var loadingMore = this.options.get('translations').get('searching'); + + var loading = { + disabled: true, + loading: true, + text: loadingMore(params) + }; + var $loading = this.option(loading); + $loading.className += ' loading-results'; + + this.$results.prepend($loading); + }; + + Results.prototype.hideLoading = function () { + this.$results.find('.loading-results').remove(); + }; + + Results.prototype.option = function (data) { + var option = document.createElement('li'); + option.className = 'select2-results__option'; + + var attrs = { + 'role': 'treeitem', + 'aria-selected': 'false' + }; + + if (data.disabled) { + delete attrs['aria-selected']; + attrs['aria-disabled'] = 'true'; + } + + if (data.id == null) { + delete attrs['aria-selected']; + } + + if (data._resultId != null) { + option.id = data._resultId; + } + + if (data.title) { + option.title = data.title; + } + + if (data.children) { + attrs.role = 'group'; + attrs['aria-label'] = data.text; + delete attrs['aria-selected']; + } + + for (var attr in attrs) { + var val = attrs[attr]; + + option.setAttribute(attr, val); + } + + if (data.children) { + var $option = $(option); + + var label = document.createElement('strong'); + label.className = 'select2-results__group'; + + var $label = $(label); + this.template(data, label); + + var $children = []; + + for (var c = 0; c < data.children.length; c++) { + var child = data.children[c]; + + var $child = this.option(child); + + $children.push($child); + } + + var $childrenContainer = $('
      ', { + 'class': 'select2-results__options select2-results__options--nested' + }); + + $childrenContainer.append($children); + + $option.append(label); + $option.append($childrenContainer); + } else { + this.template(data, option); + } + + $.data(option, 'data', data); + + return option; + }; + + Results.prototype.bind = function (container, $container) { + var self = this; + + var id = container.id + '-results'; + + this.$results.attr('id', id); + + container.on('results:all', function (params) { + self.clear(); + self.append(params.data); + + if (container.isOpen()) { + self.setClasses(); + self.highlightFirstItem(); + } + }); + + container.on('results:append', function (params) { + self.append(params.data); + + if (container.isOpen()) { + self.setClasses(); + } + }); + + container.on('query', function (params) { + self.hideMessages(); + self.showLoading(params); + }); + + container.on('select', function () { + if (!container.isOpen()) { + return; + } + + self.setClasses(); + self.highlightFirstItem(); + }); + + container.on('unselect', function () { + if (!container.isOpen()) { + return; + } + + self.setClasses(); + self.highlightFirstItem(); + }); + + container.on('open', function () { + // When the dropdown is open, aria-expended="true" + self.$results.attr('aria-expanded', 'true'); + self.$results.attr('aria-hidden', 'false'); + + self.setClasses(); + self.ensureHighlightVisible(); + }); + + container.on('close', function () { + // When the dropdown is closed, aria-expended="false" + self.$results.attr('aria-expanded', 'false'); + self.$results.attr('aria-hidden', 'true'); + self.$results.removeAttr('aria-activedescendant'); + }); + + container.on('results:toggle', function () { + var $highlighted = self.getHighlightedResults(); + + if ($highlighted.length === 0) { + return; + } + + $highlighted.trigger('mouseup'); + }); + + container.on('results:select', function () { + var $highlighted = self.getHighlightedResults(); + + if ($highlighted.length === 0) { + return; + } + + var data = $highlighted.data('data'); + + if ($highlighted.attr('aria-selected') == 'true') { + self.trigger('close', {}); + } else { + self.trigger('select', { + data: data + }); + } + }); + + container.on('results:previous', function () { + var $highlighted = self.getHighlightedResults(); + + var $options = self.$results.find('[aria-selected]'); + + var currentIndex = $options.index($highlighted); + + // If we are already at te top, don't move further + if (currentIndex === 0) { + return; + } + + var nextIndex = currentIndex - 1; + + // If none are highlighted, highlight the first + if ($highlighted.length === 0) { + nextIndex = 0; + } + + var $next = $options.eq(nextIndex); + + $next.trigger('mouseenter'); + + var currentOffset = self.$results.offset().top; + var nextTop = $next.offset().top; + var nextOffset = self.$results.scrollTop() + (nextTop - currentOffset); + + if (nextIndex === 0) { + self.$results.scrollTop(0); + } else if (nextTop - currentOffset < 0) { + self.$results.scrollTop(nextOffset); + } + }); + + container.on('results:next', function () { + var $highlighted = self.getHighlightedResults(); + + var $options = self.$results.find('[aria-selected]'); + + var currentIndex = $options.index($highlighted); + + var nextIndex = currentIndex + 1; + + // If we are at the last option, stay there + if (nextIndex >= $options.length) { + return; + } + + var $next = $options.eq(nextIndex); + + $next.trigger('mouseenter'); + + var currentOffset = self.$results.offset().top + + self.$results.outerHeight(false); + var nextBottom = $next.offset().top + $next.outerHeight(false); + var nextOffset = self.$results.scrollTop() + nextBottom - currentOffset; + + if (nextIndex === 0) { + self.$results.scrollTop(0); + } else if (nextBottom > currentOffset) { + self.$results.scrollTop(nextOffset); + } + }); + + container.on('results:focus', function (params) { + params.element.addClass('select2-results__option--highlighted'); + }); + + container.on('results:message', function (params) { + self.displayMessage(params); + }); + + if ($.fn.mousewheel) { + this.$results.on('mousewheel', function (e) { + var top = self.$results.scrollTop(); + + var bottom = self.$results.get(0).scrollHeight - top + e.deltaY; + + var isAtTop = e.deltaY > 0 && top - e.deltaY <= 0; + var isAtBottom = e.deltaY < 0 && bottom <= self.$results.height(); + + if (isAtTop) { + self.$results.scrollTop(0); + + e.preventDefault(); + e.stopPropagation(); + } else if (isAtBottom) { + self.$results.scrollTop( + self.$results.get(0).scrollHeight - self.$results.height() + ); + + e.preventDefault(); + e.stopPropagation(); + } + }); + } + + this.$results.on('mouseup', '.select2-results__option[aria-selected]', + function (evt) { + var $this = $(this); + + var data = $this.data('data'); + + if ($this.attr('aria-selected') === 'true') { + if (self.options.get('multiple')) { + self.trigger('unselect', { + originalEvent: evt, + data: data + }); + } else { + self.trigger('close', {}); + } + + return; + } + + self.trigger('select', { + originalEvent: evt, + data: data + }); + }); + + this.$results.on('mouseenter', '.select2-results__option[aria-selected]', + function (evt) { + var data = $(this).data('data'); + + self.getHighlightedResults() + .removeClass('select2-results__option--highlighted'); + + self.trigger('results:focus', { + data: data, + element: $(this) + }); + }); + }; + + Results.prototype.getHighlightedResults = function () { + var $highlighted = this.$results + .find('.select2-results__option--highlighted'); + + return $highlighted; + }; + + Results.prototype.destroy = function () { + this.$results.remove(); + }; + + Results.prototype.ensureHighlightVisible = function () { + var $highlighted = this.getHighlightedResults(); + + if ($highlighted.length === 0) { + return; + } + + var $options = this.$results.find('[aria-selected]'); + + var currentIndex = $options.index($highlighted); + + var currentOffset = this.$results.offset().top; + var nextTop = $highlighted.offset().top; + var nextOffset = this.$results.scrollTop() + (nextTop - currentOffset); + + var offsetDelta = nextTop - currentOffset; + nextOffset -= $highlighted.outerHeight(false) * 2; + + if (currentIndex <= 2) { + this.$results.scrollTop(0); + } else if (offsetDelta > this.$results.outerHeight() || offsetDelta < 0) { + this.$results.scrollTop(nextOffset); + } + }; + + Results.prototype.template = function (result, container) { + var template = this.options.get('templateResult'); + var escapeMarkup = this.options.get('escapeMarkup'); + + var content = template(result, container); + + if (content == null) { + container.style.display = 'none'; + } else if (typeof content === 'string') { + container.innerHTML = escapeMarkup(content); + } else { + $(container).append(content); + } + }; + + return Results; +}); + +S2.define('select2/keys',[ + +], function () { + var KEYS = { + BACKSPACE: 8, + TAB: 9, + ENTER: 13, + SHIFT: 16, + CTRL: 17, + ALT: 18, + ESC: 27, + SPACE: 32, + PAGE_UP: 33, + PAGE_DOWN: 34, + END: 35, + HOME: 36, + LEFT: 37, + UP: 38, + RIGHT: 39, + DOWN: 40, + DELETE: 46 + }; + + return KEYS; +}); + +S2.define('select2/selection/base',[ + 'jquery', + '../utils', + '../keys' +], function ($, Utils, KEYS) { + function BaseSelection ($element, options) { + this.$element = $element; + this.options = options; + + BaseSelection.__super__.constructor.call(this); + } + + Utils.Extend(BaseSelection, Utils.Observable); + + BaseSelection.prototype.render = function () { + var $selection = $( + '' + ); + + this._tabindex = 0; + + if (this.$element.data('old-tabindex') != null) { + this._tabindex = this.$element.data('old-tabindex'); + } else if (this.$element.attr('tabindex') != null) { + this._tabindex = this.$element.attr('tabindex'); + } + + $selection.attr('title', this.$element.attr('title')); + $selection.attr('tabindex', this._tabindex); + + this.$selection = $selection; + + return $selection; + }; + + BaseSelection.prototype.bind = function (container, $container) { + var self = this; + + var id = container.id + '-container'; + var resultsId = container.id + '-results'; + + this.container = container; + + this.$selection.on('focus', function (evt) { + self.trigger('focus', evt); + }); + + this.$selection.on('blur', function (evt) { + self._handleBlur(evt); + }); + + this.$selection.on('keydown', function (evt) { + self.trigger('keypress', evt); + + if (evt.which === KEYS.SPACE) { + evt.preventDefault(); + } + }); + + container.on('results:focus', function (params) { + self.$selection.attr('aria-activedescendant', params.data._resultId); + }); + + container.on('selection:update', function (params) { + self.update(params.data); + }); + + container.on('open', function () { + // When the dropdown is open, aria-expanded="true" + self.$selection.attr('aria-expanded', 'true'); + self.$selection.attr('aria-owns', resultsId); + + self._attachCloseHandler(container); + }); + + container.on('close', function () { + // When the dropdown is closed, aria-expanded="false" + self.$selection.attr('aria-expanded', 'false'); + self.$selection.removeAttr('aria-activedescendant'); + self.$selection.removeAttr('aria-owns'); + + self.$selection.focus(); + + self._detachCloseHandler(container); + }); + + container.on('enable', function () { + self.$selection.attr('tabindex', self._tabindex); + }); + + container.on('disable', function () { + self.$selection.attr('tabindex', '-1'); + }); + }; + + BaseSelection.prototype._handleBlur = function (evt) { + var self = this; + + // This needs to be delayed as the active element is the body when the tab + // key is pressed, possibly along with others. + window.setTimeout(function () { + // Don't trigger `blur` if the focus is still in the selection + if ( + (document.activeElement == self.$selection[0]) || + ($.contains(self.$selection[0], document.activeElement)) + ) { + return; + } + + self.trigger('blur', evt); + }, 1); + }; + + BaseSelection.prototype._attachCloseHandler = function (container) { + var self = this; + + $(document.body).on('mousedown.select2.' + container.id, function (e) { + var $target = $(e.target); + + var $select = $target.closest('.select2'); + + var $all = $('.select2.select2-container--open'); + + $all.each(function () { + var $this = $(this); + + if (this == $select[0]) { + return; + } + + var $element = $this.data('element'); + + $element.select2('close'); + }); + }); + }; + + BaseSelection.prototype._detachCloseHandler = function (container) { + $(document.body).off('mousedown.select2.' + container.id); + }; + + BaseSelection.prototype.position = function ($selection, $container) { + var $selectionContainer = $container.find('.selection'); + $selectionContainer.append($selection); + }; + + BaseSelection.prototype.destroy = function () { + this._detachCloseHandler(this.container); + }; + + BaseSelection.prototype.update = function (data) { + throw new Error('The `update` method must be defined in child classes.'); + }; + + return BaseSelection; +}); + +S2.define('select2/selection/single',[ + 'jquery', + './base', + '../utils', + '../keys' +], function ($, BaseSelection, Utils, KEYS) { + function SingleSelection () { + SingleSelection.__super__.constructor.apply(this, arguments); + } + + Utils.Extend(SingleSelection, BaseSelection); + + SingleSelection.prototype.render = function () { + var $selection = SingleSelection.__super__.render.call(this); + + $selection.addClass('select2-selection--single'); + + $selection.html( + '' + + '' + + '' + + '' + ); + + return $selection; + }; + + SingleSelection.prototype.bind = function (container, $container) { + var self = this; + + SingleSelection.__super__.bind.apply(this, arguments); + + var id = container.id + '-container'; + + this.$selection.find('.select2-selection__rendered').attr('id', id); + this.$selection.attr('aria-labelledby', id); + + this.$selection.on('mousedown', function (evt) { + // Only respond to left clicks + if (evt.which !== 1) { + return; + } + + self.trigger('toggle', { + originalEvent: evt + }); + }); + + this.$selection.on('focus', function (evt) { + // User focuses on the container + }); + + this.$selection.on('blur', function (evt) { + // User exits the container + }); + + container.on('focus', function (evt) { + if (!container.isOpen()) { + self.$selection.focus(); + } + }); + + container.on('selection:update', function (params) { + self.update(params.data); + }); + }; + + SingleSelection.prototype.clear = function () { + this.$selection.find('.select2-selection__rendered').empty(); + }; + + SingleSelection.prototype.display = function (data, container) { + var template = this.options.get('templateSelection'); + var escapeMarkup = this.options.get('escapeMarkup'); + + return escapeMarkup(template(data, container)); + }; + + SingleSelection.prototype.selectionContainer = function () { + return $(''); + }; + + SingleSelection.prototype.update = function (data) { + if (data.length === 0) { + this.clear(); + return; + } + + var selection = data[0]; + + var $rendered = this.$selection.find('.select2-selection__rendered'); + var formatted = this.display(selection, $rendered); + + $rendered.empty().append(formatted); + $rendered.prop('title', selection.title || selection.text); + }; + + return SingleSelection; +}); + +S2.define('select2/selection/multiple',[ + 'jquery', + './base', + '../utils' +], function ($, BaseSelection, Utils) { + function MultipleSelection ($element, options) { + MultipleSelection.__super__.constructor.apply(this, arguments); + } + + Utils.Extend(MultipleSelection, BaseSelection); + + MultipleSelection.prototype.render = function () { + var $selection = MultipleSelection.__super__.render.call(this); + + $selection.addClass('select2-selection--multiple'); + + $selection.html( + '
        ' + ); + + return $selection; + }; + + MultipleSelection.prototype.bind = function (container, $container) { + var self = this; + + MultipleSelection.__super__.bind.apply(this, arguments); + + this.$selection.on('click', function (evt) { + self.trigger('toggle', { + originalEvent: evt + }); + }); + + this.$selection.on( + 'click', + '.select2-selection__choice__remove', + function (evt) { + // Ignore the event if it is disabled + if (self.options.get('disabled')) { + return; + } + + var $remove = $(this); + var $selection = $remove.parent(); + + var data = $selection.data('data'); + + self.trigger('unselect', { + originalEvent: evt, + data: data + }); + } + ); + }; + + MultipleSelection.prototype.clear = function () { + this.$selection.find('.select2-selection__rendered').empty(); + }; + + MultipleSelection.prototype.display = function (data, container) { + var template = this.options.get('templateSelection'); + var escapeMarkup = this.options.get('escapeMarkup'); + + return escapeMarkup(template(data, container)); + }; + + MultipleSelection.prototype.selectionContainer = function () { + var $container = $( + '
      • ' + + '' + + '×' + + '' + + '
      • ' + ); + + return $container; + }; + + MultipleSelection.prototype.update = function (data) { + this.clear(); + + if (data.length === 0) { + return; + } + + var $selections = []; + + for (var d = 0; d < data.length; d++) { + var selection = data[d]; + + var $selection = this.selectionContainer(); + var formatted = this.display(selection, $selection); + + $selection.append(formatted); + $selection.prop('title', selection.title || selection.text); + + $selection.data('data', selection); + + $selections.push($selection); + } + + var $rendered = this.$selection.find('.select2-selection__rendered'); + + Utils.appendMany($rendered, $selections); + }; + + return MultipleSelection; +}); + +S2.define('select2/selection/placeholder',[ + '../utils' +], function (Utils) { + function Placeholder (decorated, $element, options) { + this.placeholder = this.normalizePlaceholder(options.get('placeholder')); + + decorated.call(this, $element, options); + } + + Placeholder.prototype.normalizePlaceholder = function (_, placeholder) { + if (typeof placeholder === 'string') { + placeholder = { + id: '', + text: placeholder + }; + } + + return placeholder; + }; + + Placeholder.prototype.createPlaceholder = function (decorated, placeholder) { + var $placeholder = this.selectionContainer(); + + $placeholder.html(this.display(placeholder)); + $placeholder.addClass('select2-selection__placeholder') + .removeClass('select2-selection__choice'); + + return $placeholder; + }; + + Placeholder.prototype.update = function (decorated, data) { + var singlePlaceholder = ( + data.length == 1 && data[0].id != this.placeholder.id + ); + var multipleSelections = data.length > 1; + + if (multipleSelections || singlePlaceholder) { + return decorated.call(this, data); + } + + this.clear(); + + var $placeholder = this.createPlaceholder(this.placeholder); + + this.$selection.find('.select2-selection__rendered').append($placeholder); + }; + + return Placeholder; +}); + +S2.define('select2/selection/allowClear',[ + 'jquery', + '../keys' +], function ($, KEYS) { + function AllowClear () { } + + AllowClear.prototype.bind = function (decorated, container, $container) { + var self = this; + + decorated.call(this, container, $container); + + if (this.placeholder == null) { + if (this.options.get('debug') && window.console && console.error) { + console.error( + 'Select2: The `allowClear` option should be used in combination ' + + 'with the `placeholder` option.' + ); + } + } + + this.$selection.on('mousedown', '.select2-selection__clear', + function (evt) { + self._handleClear(evt); + }); + + container.on('keypress', function (evt) { + self._handleKeyboardClear(evt, container); + }); + }; + + AllowClear.prototype._handleClear = function (_, evt) { + // Ignore the event if it is disabled + if (this.options.get('disabled')) { + return; + } + + var $clear = this.$selection.find('.select2-selection__clear'); + + // Ignore the event if nothing has been selected + if ($clear.length === 0) { + return; + } + + evt.stopPropagation(); + + var data = $clear.data('data'); + + for (var d = 0; d < data.length; d++) { + var unselectData = { + data: data[d] + }; + + // Trigger the `unselect` event, so people can prevent it from being + // cleared. + this.trigger('unselect', unselectData); + + // If the event was prevented, don't clear it out. + if (unselectData.prevented) { + return; + } + } + + this.$element.val(this.placeholder.id).trigger('change'); + + this.trigger('toggle', {}); + }; + + AllowClear.prototype._handleKeyboardClear = function (_, evt, container) { + if (container.isOpen()) { + return; + } + + if (evt.which == KEYS.DELETE || evt.which == KEYS.BACKSPACE) { + this._handleClear(evt); + } + }; + + AllowClear.prototype.update = function (decorated, data) { + decorated.call(this, data); + + if (this.$selection.find('.select2-selection__placeholder').length > 0 || + data.length === 0) { + return; + } + + var $remove = $( + '' + + '×' + + '' + ); + $remove.data('data', data); + + this.$selection.find('.select2-selection__rendered').prepend($remove); + }; + + return AllowClear; +}); + +S2.define('select2/selection/search',[ + 'jquery', + '../utils', + '../keys' +], function ($, Utils, KEYS) { + function Search (decorated, $element, options) { + decorated.call(this, $element, options); + } + + Search.prototype.render = function (decorated) { + var $search = $( + '' + ); + + this.$searchContainer = $search; + this.$search = $search.find('input'); + + var $rendered = decorated.call(this); + + this._transferTabIndex(); + + return $rendered; + }; + + Search.prototype.bind = function (decorated, container, $container) { + var self = this; + + decorated.call(this, container, $container); + + container.on('open', function () { + self.$search.trigger('focus'); + }); + + container.on('close', function () { + self.$search.val(''); + self.$search.removeAttr('aria-activedescendant'); + self.$search.trigger('focus'); + }); + + container.on('enable', function () { + self.$search.prop('disabled', false); + + self._transferTabIndex(); + }); + + container.on('disable', function () { + self.$search.prop('disabled', true); + }); + + container.on('focus', function (evt) { + self.$search.trigger('focus'); + }); + + container.on('results:focus', function (params) { + self.$search.attr('aria-activedescendant', params.id); + }); + + this.$selection.on('focusin', '.select2-search--inline', function (evt) { + self.trigger('focus', evt); + }); + + this.$selection.on('focusout', '.select2-search--inline', function (evt) { + self._handleBlur(evt); + }); + + this.$selection.on('keydown', '.select2-search--inline', function (evt) { + evt.stopPropagation(); + + self.trigger('keypress', evt); + + self._keyUpPrevented = evt.isDefaultPrevented(); + + var key = evt.which; + + if (key === KEYS.BACKSPACE && self.$search.val() === '') { + var $previousChoice = self.$searchContainer + .prev('.select2-selection__choice'); + + if ($previousChoice.length > 0) { + var item = $previousChoice.data('data'); + + self.searchRemoveChoice(item); + + evt.preventDefault(); + } + } + }); + + // Try to detect the IE version should the `documentMode` property that + // is stored on the document. This is only implemented in IE and is + // slightly cleaner than doing a user agent check. + // This property is not available in Edge, but Edge also doesn't have + // this bug. + var msie = document.documentMode; + var disableInputEvents = msie && msie <= 11; + + // Workaround for browsers which do not support the `input` event + // This will prevent double-triggering of events for browsers which support + // both the `keyup` and `input` events. + this.$selection.on( + 'input.searchcheck', + '.select2-search--inline', + function (evt) { + // IE will trigger the `input` event when a placeholder is used on a + // search box. To get around this issue, we are forced to ignore all + // `input` events in IE and keep using `keyup`. + if (disableInputEvents) { + self.$selection.off('input.search input.searchcheck'); + return; + } + + // Unbind the duplicated `keyup` event + self.$selection.off('keyup.search'); + } + ); + + this.$selection.on( + 'keyup.search input.search', + '.select2-search--inline', + function (evt) { + // IE will trigger the `input` event when a placeholder is used on a + // search box. To get around this issue, we are forced to ignore all + // `input` events in IE and keep using `keyup`. + if (disableInputEvents && evt.type === 'input') { + self.$selection.off('input.search input.searchcheck'); + return; + } + + var key = evt.which; + + // We can freely ignore events from modifier keys + if (key == KEYS.SHIFT || key == KEYS.CTRL || key == KEYS.ALT) { + return; + } + + // Tabbing will be handled during the `keydown` phase + if (key == KEYS.TAB) { + return; + } + + self.handleSearch(evt); + } + ); + }; + + /** + * This method will transfer the tabindex attribute from the rendered + * selection to the search box. This allows for the search box to be used as + * the primary focus instead of the selection container. + * + * @private + */ + Search.prototype._transferTabIndex = function (decorated) { + this.$search.attr('tabindex', this.$selection.attr('tabindex')); + this.$selection.attr('tabindex', '-1'); + }; + + Search.prototype.createPlaceholder = function (decorated, placeholder) { + this.$search.attr('placeholder', placeholder.text); + }; + + Search.prototype.update = function (decorated, data) { + var searchHadFocus = this.$search[0] == document.activeElement; + + this.$search.attr('placeholder', ''); + + decorated.call(this, data); + + this.$selection.find('.select2-selection__rendered') + .append(this.$searchContainer); + + this.resizeSearch(); + if (searchHadFocus) { + this.$search.focus(); + } + }; + + Search.prototype.handleSearch = function () { + this.resizeSearch(); + + if (!this._keyUpPrevented) { + var input = this.$search.val(); + + this.trigger('query', { + term: input + }); + } + + this._keyUpPrevented = false; + }; + + Search.prototype.searchRemoveChoice = function (decorated, item) { + this.trigger('unselect', { + data: item + }); + + this.$search.val(item.text); + this.handleSearch(); + }; + + Search.prototype.resizeSearch = function () { + this.$search.css('width', '25px'); + + var width = ''; + + if (this.$search.attr('placeholder') !== '') { + width = this.$selection.find('.select2-selection__rendered').innerWidth(); + } else { + var minimumWidth = this.$search.val().length + 1; + + width = (minimumWidth * 0.75) + 'em'; + } + + this.$search.css('width', width); + }; + + return Search; +}); + +S2.define('select2/selection/eventRelay',[ + 'jquery' +], function ($) { + function EventRelay () { } + + EventRelay.prototype.bind = function (decorated, container, $container) { + var self = this; + var relayEvents = [ + 'open', 'opening', + 'close', 'closing', + 'select', 'selecting', + 'unselect', 'unselecting' + ]; + + var preventableEvents = ['opening', 'closing', 'selecting', 'unselecting']; + + decorated.call(this, container, $container); + + container.on('*', function (name, params) { + // Ignore events that should not be relayed + if ($.inArray(name, relayEvents) === -1) { + return; + } + + // The parameters should always be an object + params = params || {}; + + // Generate the jQuery event for the Select2 event + var evt = $.Event('select2:' + name, { + params: params + }); + + self.$element.trigger(evt); + + // Only handle preventable events if it was one + if ($.inArray(name, preventableEvents) === -1) { + return; + } + + params.prevented = evt.isDefaultPrevented(); + }); + }; + + return EventRelay; +}); + +S2.define('select2/translation',[ + 'jquery', + 'require' +], function ($, require) { + function Translation (dict) { + this.dict = dict || {}; + } + + Translation.prototype.all = function () { + return this.dict; + }; + + Translation.prototype.get = function (key) { + return this.dict[key]; + }; + + Translation.prototype.extend = function (translation) { + this.dict = $.extend({}, translation.all(), this.dict); + }; + + // Static functions + + Translation._cache = {}; + + Translation.loadPath = function (path) { + if (!(path in Translation._cache)) { + var translations = require(path); + + Translation._cache[path] = translations; + } + + return new Translation(Translation._cache[path]); + }; + + return Translation; +}); + +S2.define('select2/diacritics',[ + +], function () { + var diacritics = { + '\u24B6': 'A', + '\uFF21': 'A', + '\u00C0': 'A', + '\u00C1': 'A', + '\u00C2': 'A', + '\u1EA6': 'A', + '\u1EA4': 'A', + '\u1EAA': 'A', + '\u1EA8': 'A', + '\u00C3': 'A', + '\u0100': 'A', + '\u0102': 'A', + '\u1EB0': 'A', + '\u1EAE': 'A', + '\u1EB4': 'A', + '\u1EB2': 'A', + '\u0226': 'A', + '\u01E0': 'A', + '\u00C4': 'A', + '\u01DE': 'A', + '\u1EA2': 'A', + '\u00C5': 'A', + '\u01FA': 'A', + '\u01CD': 'A', + '\u0200': 'A', + '\u0202': 'A', + '\u1EA0': 'A', + '\u1EAC': 'A', + '\u1EB6': 'A', + '\u1E00': 'A', + '\u0104': 'A', + '\u023A': 'A', + '\u2C6F': 'A', + '\uA732': 'AA', + '\u00C6': 'AE', + '\u01FC': 'AE', + '\u01E2': 'AE', + '\uA734': 'AO', + '\uA736': 'AU', + '\uA738': 'AV', + '\uA73A': 'AV', + '\uA73C': 'AY', + '\u24B7': 'B', + '\uFF22': 'B', + '\u1E02': 'B', + '\u1E04': 'B', + '\u1E06': 'B', + '\u0243': 'B', + '\u0182': 'B', + '\u0181': 'B', + '\u24B8': 'C', + '\uFF23': 'C', + '\u0106': 'C', + '\u0108': 'C', + '\u010A': 'C', + '\u010C': 'C', + '\u00C7': 'C', + '\u1E08': 'C', + '\u0187': 'C', + '\u023B': 'C', + '\uA73E': 'C', + '\u24B9': 'D', + '\uFF24': 'D', + '\u1E0A': 'D', + '\u010E': 'D', + '\u1E0C': 'D', + '\u1E10': 'D', + '\u1E12': 'D', + '\u1E0E': 'D', + '\u0110': 'D', + '\u018B': 'D', + '\u018A': 'D', + '\u0189': 'D', + '\uA779': 'D', + '\u01F1': 'DZ', + '\u01C4': 'DZ', + '\u01F2': 'Dz', + '\u01C5': 'Dz', + '\u24BA': 'E', + '\uFF25': 'E', + '\u00C8': 'E', + '\u00C9': 'E', + '\u00CA': 'E', + '\u1EC0': 'E', + '\u1EBE': 'E', + '\u1EC4': 'E', + '\u1EC2': 'E', + '\u1EBC': 'E', + '\u0112': 'E', + '\u1E14': 'E', + '\u1E16': 'E', + '\u0114': 'E', + '\u0116': 'E', + '\u00CB': 'E', + '\u1EBA': 'E', + '\u011A': 'E', + '\u0204': 'E', + '\u0206': 'E', + '\u1EB8': 'E', + '\u1EC6': 'E', + '\u0228': 'E', + '\u1E1C': 'E', + '\u0118': 'E', + '\u1E18': 'E', + '\u1E1A': 'E', + '\u0190': 'E', + '\u018E': 'E', + '\u24BB': 'F', + '\uFF26': 'F', + '\u1E1E': 'F', + '\u0191': 'F', + '\uA77B': 'F', + '\u24BC': 'G', + '\uFF27': 'G', + '\u01F4': 'G', + '\u011C': 'G', + '\u1E20': 'G', + '\u011E': 'G', + '\u0120': 'G', + '\u01E6': 'G', + '\u0122': 'G', + '\u01E4': 'G', + '\u0193': 'G', + '\uA7A0': 'G', + '\uA77D': 'G', + '\uA77E': 'G', + '\u24BD': 'H', + '\uFF28': 'H', + '\u0124': 'H', + '\u1E22': 'H', + '\u1E26': 'H', + '\u021E': 'H', + '\u1E24': 'H', + '\u1E28': 'H', + '\u1E2A': 'H', + '\u0126': 'H', + '\u2C67': 'H', + '\u2C75': 'H', + '\uA78D': 'H', + '\u24BE': 'I', + '\uFF29': 'I', + '\u00CC': 'I', + '\u00CD': 'I', + '\u00CE': 'I', + '\u0128': 'I', + '\u012A': 'I', + '\u012C': 'I', + '\u0130': 'I', + '\u00CF': 'I', + '\u1E2E': 'I', + '\u1EC8': 'I', + '\u01CF': 'I', + '\u0208': 'I', + '\u020A': 'I', + '\u1ECA': 'I', + '\u012E': 'I', + '\u1E2C': 'I', + '\u0197': 'I', + '\u24BF': 'J', + '\uFF2A': 'J', + '\u0134': 'J', + '\u0248': 'J', + '\u24C0': 'K', + '\uFF2B': 'K', + '\u1E30': 'K', + '\u01E8': 'K', + '\u1E32': 'K', + '\u0136': 'K', + '\u1E34': 'K', + '\u0198': 'K', + '\u2C69': 'K', + '\uA740': 'K', + '\uA742': 'K', + '\uA744': 'K', + '\uA7A2': 'K', + '\u24C1': 'L', + '\uFF2C': 'L', + '\u013F': 'L', + '\u0139': 'L', + '\u013D': 'L', + '\u1E36': 'L', + '\u1E38': 'L', + '\u013B': 'L', + '\u1E3C': 'L', + '\u1E3A': 'L', + '\u0141': 'L', + '\u023D': 'L', + '\u2C62': 'L', + '\u2C60': 'L', + '\uA748': 'L', + '\uA746': 'L', + '\uA780': 'L', + '\u01C7': 'LJ', + '\u01C8': 'Lj', + '\u24C2': 'M', + '\uFF2D': 'M', + '\u1E3E': 'M', + '\u1E40': 'M', + '\u1E42': 'M', + '\u2C6E': 'M', + '\u019C': 'M', + '\u24C3': 'N', + '\uFF2E': 'N', + '\u01F8': 'N', + '\u0143': 'N', + '\u00D1': 'N', + '\u1E44': 'N', + '\u0147': 'N', + '\u1E46': 'N', + '\u0145': 'N', + '\u1E4A': 'N', + '\u1E48': 'N', + '\u0220': 'N', + '\u019D': 'N', + '\uA790': 'N', + '\uA7A4': 'N', + '\u01CA': 'NJ', + '\u01CB': 'Nj', + '\u24C4': 'O', + '\uFF2F': 'O', + '\u00D2': 'O', + '\u00D3': 'O', + '\u00D4': 'O', + '\u1ED2': 'O', + '\u1ED0': 'O', + '\u1ED6': 'O', + '\u1ED4': 'O', + '\u00D5': 'O', + '\u1E4C': 'O', + '\u022C': 'O', + '\u1E4E': 'O', + '\u014C': 'O', + '\u1E50': 'O', + '\u1E52': 'O', + '\u014E': 'O', + '\u022E': 'O', + '\u0230': 'O', + '\u00D6': 'O', + '\u022A': 'O', + '\u1ECE': 'O', + '\u0150': 'O', + '\u01D1': 'O', + '\u020C': 'O', + '\u020E': 'O', + '\u01A0': 'O', + '\u1EDC': 'O', + '\u1EDA': 'O', + '\u1EE0': 'O', + '\u1EDE': 'O', + '\u1EE2': 'O', + '\u1ECC': 'O', + '\u1ED8': 'O', + '\u01EA': 'O', + '\u01EC': 'O', + '\u00D8': 'O', + '\u01FE': 'O', + '\u0186': 'O', + '\u019F': 'O', + '\uA74A': 'O', + '\uA74C': 'O', + '\u01A2': 'OI', + '\uA74E': 'OO', + '\u0222': 'OU', + '\u24C5': 'P', + '\uFF30': 'P', + '\u1E54': 'P', + '\u1E56': 'P', + '\u01A4': 'P', + '\u2C63': 'P', + '\uA750': 'P', + '\uA752': 'P', + '\uA754': 'P', + '\u24C6': 'Q', + '\uFF31': 'Q', + '\uA756': 'Q', + '\uA758': 'Q', + '\u024A': 'Q', + '\u24C7': 'R', + '\uFF32': 'R', + '\u0154': 'R', + '\u1E58': 'R', + '\u0158': 'R', + '\u0210': 'R', + '\u0212': 'R', + '\u1E5A': 'R', + '\u1E5C': 'R', + '\u0156': 'R', + '\u1E5E': 'R', + '\u024C': 'R', + '\u2C64': 'R', + '\uA75A': 'R', + '\uA7A6': 'R', + '\uA782': 'R', + '\u24C8': 'S', + '\uFF33': 'S', + '\u1E9E': 'S', + '\u015A': 'S', + '\u1E64': 'S', + '\u015C': 'S', + '\u1E60': 'S', + '\u0160': 'S', + '\u1E66': 'S', + '\u1E62': 'S', + '\u1E68': 'S', + '\u0218': 'S', + '\u015E': 'S', + '\u2C7E': 'S', + '\uA7A8': 'S', + '\uA784': 'S', + '\u24C9': 'T', + '\uFF34': 'T', + '\u1E6A': 'T', + '\u0164': 'T', + '\u1E6C': 'T', + '\u021A': 'T', + '\u0162': 'T', + '\u1E70': 'T', + '\u1E6E': 'T', + '\u0166': 'T', + '\u01AC': 'T', + '\u01AE': 'T', + '\u023E': 'T', + '\uA786': 'T', + '\uA728': 'TZ', + '\u24CA': 'U', + '\uFF35': 'U', + '\u00D9': 'U', + '\u00DA': 'U', + '\u00DB': 'U', + '\u0168': 'U', + '\u1E78': 'U', + '\u016A': 'U', + '\u1E7A': 'U', + '\u016C': 'U', + '\u00DC': 'U', + '\u01DB': 'U', + '\u01D7': 'U', + '\u01D5': 'U', + '\u01D9': 'U', + '\u1EE6': 'U', + '\u016E': 'U', + '\u0170': 'U', + '\u01D3': 'U', + '\u0214': 'U', + '\u0216': 'U', + '\u01AF': 'U', + '\u1EEA': 'U', + '\u1EE8': 'U', + '\u1EEE': 'U', + '\u1EEC': 'U', + '\u1EF0': 'U', + '\u1EE4': 'U', + '\u1E72': 'U', + '\u0172': 'U', + '\u1E76': 'U', + '\u1E74': 'U', + '\u0244': 'U', + '\u24CB': 'V', + '\uFF36': 'V', + '\u1E7C': 'V', + '\u1E7E': 'V', + '\u01B2': 'V', + '\uA75E': 'V', + '\u0245': 'V', + '\uA760': 'VY', + '\u24CC': 'W', + '\uFF37': 'W', + '\u1E80': 'W', + '\u1E82': 'W', + '\u0174': 'W', + '\u1E86': 'W', + '\u1E84': 'W', + '\u1E88': 'W', + '\u2C72': 'W', + '\u24CD': 'X', + '\uFF38': 'X', + '\u1E8A': 'X', + '\u1E8C': 'X', + '\u24CE': 'Y', + '\uFF39': 'Y', + '\u1EF2': 'Y', + '\u00DD': 'Y', + '\u0176': 'Y', + '\u1EF8': 'Y', + '\u0232': 'Y', + '\u1E8E': 'Y', + '\u0178': 'Y', + '\u1EF6': 'Y', + '\u1EF4': 'Y', + '\u01B3': 'Y', + '\u024E': 'Y', + '\u1EFE': 'Y', + '\u24CF': 'Z', + '\uFF3A': 'Z', + '\u0179': 'Z', + '\u1E90': 'Z', + '\u017B': 'Z', + '\u017D': 'Z', + '\u1E92': 'Z', + '\u1E94': 'Z', + '\u01B5': 'Z', + '\u0224': 'Z', + '\u2C7F': 'Z', + '\u2C6B': 'Z', + '\uA762': 'Z', + '\u24D0': 'a', + '\uFF41': 'a', + '\u1E9A': 'a', + '\u00E0': 'a', + '\u00E1': 'a', + '\u00E2': 'a', + '\u1EA7': 'a', + '\u1EA5': 'a', + '\u1EAB': 'a', + '\u1EA9': 'a', + '\u00E3': 'a', + '\u0101': 'a', + '\u0103': 'a', + '\u1EB1': 'a', + '\u1EAF': 'a', + '\u1EB5': 'a', + '\u1EB3': 'a', + '\u0227': 'a', + '\u01E1': 'a', + '\u00E4': 'a', + '\u01DF': 'a', + '\u1EA3': 'a', + '\u00E5': 'a', + '\u01FB': 'a', + '\u01CE': 'a', + '\u0201': 'a', + '\u0203': 'a', + '\u1EA1': 'a', + '\u1EAD': 'a', + '\u1EB7': 'a', + '\u1E01': 'a', + '\u0105': 'a', + '\u2C65': 'a', + '\u0250': 'a', + '\uA733': 'aa', + '\u00E6': 'ae', + '\u01FD': 'ae', + '\u01E3': 'ae', + '\uA735': 'ao', + '\uA737': 'au', + '\uA739': 'av', + '\uA73B': 'av', + '\uA73D': 'ay', + '\u24D1': 'b', + '\uFF42': 'b', + '\u1E03': 'b', + '\u1E05': 'b', + '\u1E07': 'b', + '\u0180': 'b', + '\u0183': 'b', + '\u0253': 'b', + '\u24D2': 'c', + '\uFF43': 'c', + '\u0107': 'c', + '\u0109': 'c', + '\u010B': 'c', + '\u010D': 'c', + '\u00E7': 'c', + '\u1E09': 'c', + '\u0188': 'c', + '\u023C': 'c', + '\uA73F': 'c', + '\u2184': 'c', + '\u24D3': 'd', + '\uFF44': 'd', + '\u1E0B': 'd', + '\u010F': 'd', + '\u1E0D': 'd', + '\u1E11': 'd', + '\u1E13': 'd', + '\u1E0F': 'd', + '\u0111': 'd', + '\u018C': 'd', + '\u0256': 'd', + '\u0257': 'd', + '\uA77A': 'd', + '\u01F3': 'dz', + '\u01C6': 'dz', + '\u24D4': 'e', + '\uFF45': 'e', + '\u00E8': 'e', + '\u00E9': 'e', + '\u00EA': 'e', + '\u1EC1': 'e', + '\u1EBF': 'e', + '\u1EC5': 'e', + '\u1EC3': 'e', + '\u1EBD': 'e', + '\u0113': 'e', + '\u1E15': 'e', + '\u1E17': 'e', + '\u0115': 'e', + '\u0117': 'e', + '\u00EB': 'e', + '\u1EBB': 'e', + '\u011B': 'e', + '\u0205': 'e', + '\u0207': 'e', + '\u1EB9': 'e', + '\u1EC7': 'e', + '\u0229': 'e', + '\u1E1D': 'e', + '\u0119': 'e', + '\u1E19': 'e', + '\u1E1B': 'e', + '\u0247': 'e', + '\u025B': 'e', + '\u01DD': 'e', + '\u24D5': 'f', + '\uFF46': 'f', + '\u1E1F': 'f', + '\u0192': 'f', + '\uA77C': 'f', + '\u24D6': 'g', + '\uFF47': 'g', + '\u01F5': 'g', + '\u011D': 'g', + '\u1E21': 'g', + '\u011F': 'g', + '\u0121': 'g', + '\u01E7': 'g', + '\u0123': 'g', + '\u01E5': 'g', + '\u0260': 'g', + '\uA7A1': 'g', + '\u1D79': 'g', + '\uA77F': 'g', + '\u24D7': 'h', + '\uFF48': 'h', + '\u0125': 'h', + '\u1E23': 'h', + '\u1E27': 'h', + '\u021F': 'h', + '\u1E25': 'h', + '\u1E29': 'h', + '\u1E2B': 'h', + '\u1E96': 'h', + '\u0127': 'h', + '\u2C68': 'h', + '\u2C76': 'h', + '\u0265': 'h', + '\u0195': 'hv', + '\u24D8': 'i', + '\uFF49': 'i', + '\u00EC': 'i', + '\u00ED': 'i', + '\u00EE': 'i', + '\u0129': 'i', + '\u012B': 'i', + '\u012D': 'i', + '\u00EF': 'i', + '\u1E2F': 'i', + '\u1EC9': 'i', + '\u01D0': 'i', + '\u0209': 'i', + '\u020B': 'i', + '\u1ECB': 'i', + '\u012F': 'i', + '\u1E2D': 'i', + '\u0268': 'i', + '\u0131': 'i', + '\u24D9': 'j', + '\uFF4A': 'j', + '\u0135': 'j', + '\u01F0': 'j', + '\u0249': 'j', + '\u24DA': 'k', + '\uFF4B': 'k', + '\u1E31': 'k', + '\u01E9': 'k', + '\u1E33': 'k', + '\u0137': 'k', + '\u1E35': 'k', + '\u0199': 'k', + '\u2C6A': 'k', + '\uA741': 'k', + '\uA743': 'k', + '\uA745': 'k', + '\uA7A3': 'k', + '\u24DB': 'l', + '\uFF4C': 'l', + '\u0140': 'l', + '\u013A': 'l', + '\u013E': 'l', + '\u1E37': 'l', + '\u1E39': 'l', + '\u013C': 'l', + '\u1E3D': 'l', + '\u1E3B': 'l', + '\u017F': 'l', + '\u0142': 'l', + '\u019A': 'l', + '\u026B': 'l', + '\u2C61': 'l', + '\uA749': 'l', + '\uA781': 'l', + '\uA747': 'l', + '\u01C9': 'lj', + '\u24DC': 'm', + '\uFF4D': 'm', + '\u1E3F': 'm', + '\u1E41': 'm', + '\u1E43': 'm', + '\u0271': 'm', + '\u026F': 'm', + '\u24DD': 'n', + '\uFF4E': 'n', + '\u01F9': 'n', + '\u0144': 'n', + '\u00F1': 'n', + '\u1E45': 'n', + '\u0148': 'n', + '\u1E47': 'n', + '\u0146': 'n', + '\u1E4B': 'n', + '\u1E49': 'n', + '\u019E': 'n', + '\u0272': 'n', + '\u0149': 'n', + '\uA791': 'n', + '\uA7A5': 'n', + '\u01CC': 'nj', + '\u24DE': 'o', + '\uFF4F': 'o', + '\u00F2': 'o', + '\u00F3': 'o', + '\u00F4': 'o', + '\u1ED3': 'o', + '\u1ED1': 'o', + '\u1ED7': 'o', + '\u1ED5': 'o', + '\u00F5': 'o', + '\u1E4D': 'o', + '\u022D': 'o', + '\u1E4F': 'o', + '\u014D': 'o', + '\u1E51': 'o', + '\u1E53': 'o', + '\u014F': 'o', + '\u022F': 'o', + '\u0231': 'o', + '\u00F6': 'o', + '\u022B': 'o', + '\u1ECF': 'o', + '\u0151': 'o', + '\u01D2': 'o', + '\u020D': 'o', + '\u020F': 'o', + '\u01A1': 'o', + '\u1EDD': 'o', + '\u1EDB': 'o', + '\u1EE1': 'o', + '\u1EDF': 'o', + '\u1EE3': 'o', + '\u1ECD': 'o', + '\u1ED9': 'o', + '\u01EB': 'o', + '\u01ED': 'o', + '\u00F8': 'o', + '\u01FF': 'o', + '\u0254': 'o', + '\uA74B': 'o', + '\uA74D': 'o', + '\u0275': 'o', + '\u01A3': 'oi', + '\u0223': 'ou', + '\uA74F': 'oo', + '\u24DF': 'p', + '\uFF50': 'p', + '\u1E55': 'p', + '\u1E57': 'p', + '\u01A5': 'p', + '\u1D7D': 'p', + '\uA751': 'p', + '\uA753': 'p', + '\uA755': 'p', + '\u24E0': 'q', + '\uFF51': 'q', + '\u024B': 'q', + '\uA757': 'q', + '\uA759': 'q', + '\u24E1': 'r', + '\uFF52': 'r', + '\u0155': 'r', + '\u1E59': 'r', + '\u0159': 'r', + '\u0211': 'r', + '\u0213': 'r', + '\u1E5B': 'r', + '\u1E5D': 'r', + '\u0157': 'r', + '\u1E5F': 'r', + '\u024D': 'r', + '\u027D': 'r', + '\uA75B': 'r', + '\uA7A7': 'r', + '\uA783': 'r', + '\u24E2': 's', + '\uFF53': 's', + '\u00DF': 's', + '\u015B': 's', + '\u1E65': 's', + '\u015D': 's', + '\u1E61': 's', + '\u0161': 's', + '\u1E67': 's', + '\u1E63': 's', + '\u1E69': 's', + '\u0219': 's', + '\u015F': 's', + '\u023F': 's', + '\uA7A9': 's', + '\uA785': 's', + '\u1E9B': 's', + '\u24E3': 't', + '\uFF54': 't', + '\u1E6B': 't', + '\u1E97': 't', + '\u0165': 't', + '\u1E6D': 't', + '\u021B': 't', + '\u0163': 't', + '\u1E71': 't', + '\u1E6F': 't', + '\u0167': 't', + '\u01AD': 't', + '\u0288': 't', + '\u2C66': 't', + '\uA787': 't', + '\uA729': 'tz', + '\u24E4': 'u', + '\uFF55': 'u', + '\u00F9': 'u', + '\u00FA': 'u', + '\u00FB': 'u', + '\u0169': 'u', + '\u1E79': 'u', + '\u016B': 'u', + '\u1E7B': 'u', + '\u016D': 'u', + '\u00FC': 'u', + '\u01DC': 'u', + '\u01D8': 'u', + '\u01D6': 'u', + '\u01DA': 'u', + '\u1EE7': 'u', + '\u016F': 'u', + '\u0171': 'u', + '\u01D4': 'u', + '\u0215': 'u', + '\u0217': 'u', + '\u01B0': 'u', + '\u1EEB': 'u', + '\u1EE9': 'u', + '\u1EEF': 'u', + '\u1EED': 'u', + '\u1EF1': 'u', + '\u1EE5': 'u', + '\u1E73': 'u', + '\u0173': 'u', + '\u1E77': 'u', + '\u1E75': 'u', + '\u0289': 'u', + '\u24E5': 'v', + '\uFF56': 'v', + '\u1E7D': 'v', + '\u1E7F': 'v', + '\u028B': 'v', + '\uA75F': 'v', + '\u028C': 'v', + '\uA761': 'vy', + '\u24E6': 'w', + '\uFF57': 'w', + '\u1E81': 'w', + '\u1E83': 'w', + '\u0175': 'w', + '\u1E87': 'w', + '\u1E85': 'w', + '\u1E98': 'w', + '\u1E89': 'w', + '\u2C73': 'w', + '\u24E7': 'x', + '\uFF58': 'x', + '\u1E8B': 'x', + '\u1E8D': 'x', + '\u24E8': 'y', + '\uFF59': 'y', + '\u1EF3': 'y', + '\u00FD': 'y', + '\u0177': 'y', + '\u1EF9': 'y', + '\u0233': 'y', + '\u1E8F': 'y', + '\u00FF': 'y', + '\u1EF7': 'y', + '\u1E99': 'y', + '\u1EF5': 'y', + '\u01B4': 'y', + '\u024F': 'y', + '\u1EFF': 'y', + '\u24E9': 'z', + '\uFF5A': 'z', + '\u017A': 'z', + '\u1E91': 'z', + '\u017C': 'z', + '\u017E': 'z', + '\u1E93': 'z', + '\u1E95': 'z', + '\u01B6': 'z', + '\u0225': 'z', + '\u0240': 'z', + '\u2C6C': 'z', + '\uA763': 'z', + '\u0386': '\u0391', + '\u0388': '\u0395', + '\u0389': '\u0397', + '\u038A': '\u0399', + '\u03AA': '\u0399', + '\u038C': '\u039F', + '\u038E': '\u03A5', + '\u03AB': '\u03A5', + '\u038F': '\u03A9', + '\u03AC': '\u03B1', + '\u03AD': '\u03B5', + '\u03AE': '\u03B7', + '\u03AF': '\u03B9', + '\u03CA': '\u03B9', + '\u0390': '\u03B9', + '\u03CC': '\u03BF', + '\u03CD': '\u03C5', + '\u03CB': '\u03C5', + '\u03B0': '\u03C5', + '\u03C9': '\u03C9', + '\u03C2': '\u03C3' + }; + + return diacritics; +}); + +S2.define('select2/data/base',[ + '../utils' +], function (Utils) { + function BaseAdapter ($element, options) { + BaseAdapter.__super__.constructor.call(this); + } + + Utils.Extend(BaseAdapter, Utils.Observable); + + BaseAdapter.prototype.current = function (callback) { + throw new Error('The `current` method must be defined in child classes.'); + }; + + BaseAdapter.prototype.query = function (params, callback) { + throw new Error('The `query` method must be defined in child classes.'); + }; + + BaseAdapter.prototype.bind = function (container, $container) { + // Can be implemented in subclasses + }; + + BaseAdapter.prototype.destroy = function () { + // Can be implemented in subclasses + }; + + BaseAdapter.prototype.generateResultId = function (container, data) { + var id = container.id + '-result-'; + + id += Utils.generateChars(4); + + if (data.id != null) { + id += '-' + data.id.toString(); + } else { + id += '-' + Utils.generateChars(4); + } + return id; + }; + + return BaseAdapter; +}); + +S2.define('select2/data/select',[ + './base', + '../utils', + 'jquery' +], function (BaseAdapter, Utils, $) { + function SelectAdapter ($element, options) { + this.$element = $element; + this.options = options; + + SelectAdapter.__super__.constructor.call(this); + } + + Utils.Extend(SelectAdapter, BaseAdapter); + + SelectAdapter.prototype.current = function (callback) { + var data = []; + var self = this; + + this.$element.find(':selected').each(function () { + var $option = $(this); + + var option = self.item($option); + + data.push(option); + }); + + callback(data); + }; + + SelectAdapter.prototype.select = function (data) { + var self = this; + + data.selected = true; + + // If data.element is a DOM node, use it instead + if ($(data.element).is('option')) { + data.element.selected = true; + + this.$element.trigger('change'); + + return; + } + + if (this.$element.prop('multiple')) { + this.current(function (currentData) { + var val = []; + + data = [data]; + data.push.apply(data, currentData); + + for (var d = 0; d < data.length; d++) { + var id = data[d].id; + + if ($.inArray(id, val) === -1) { + val.push(id); + } + } + + self.$element.val(val); + self.$element.trigger('change'); + }); + } else { + var val = data.id; + + this.$element.val(val); + this.$element.trigger('change'); + } + }; + + SelectAdapter.prototype.unselect = function (data) { + var self = this; + + if (!this.$element.prop('multiple')) { + return; + } + + data.selected = false; + + if ($(data.element).is('option')) { + data.element.selected = false; + + this.$element.trigger('change'); + + return; + } + + this.current(function (currentData) { + var val = []; + + for (var d = 0; d < currentData.length; d++) { + var id = currentData[d].id; + + if (id !== data.id && $.inArray(id, val) === -1) { + val.push(id); + } + } + + self.$element.val(val); + + self.$element.trigger('change'); + }); + }; + + SelectAdapter.prototype.bind = function (container, $container) { + var self = this; + + this.container = container; + + container.on('select', function (params) { + self.select(params.data); + }); + + container.on('unselect', function (params) { + self.unselect(params.data); + }); + }; + + SelectAdapter.prototype.destroy = function () { + // Remove anything added to child elements + this.$element.find('*').each(function () { + // Remove any custom data set by Select2 + $.removeData(this, 'data'); + }); + }; + + SelectAdapter.prototype.query = function (params, callback) { + var data = []; + var self = this; + + var $options = this.$element.children(); + + $options.each(function () { + var $option = $(this); + + if (!$option.is('option') && !$option.is('optgroup')) { + return; + } + + var option = self.item($option); + + var matches = self.matches(params, option); + + if (matches !== null) { + data.push(matches); + } + }); + + callback({ + results: data + }); + }; + + SelectAdapter.prototype.addOptions = function ($options) { + Utils.appendMany(this.$element, $options); + }; + + SelectAdapter.prototype.option = function (data) { + var option; + + if (data.children) { + option = document.createElement('optgroup'); + option.label = data.text; + } else { + option = document.createElement('option'); + + if (option.textContent !== undefined) { + option.textContent = data.text; + } else { + option.innerText = data.text; + } + } + + if (data.id) { + option.value = data.id; + } + + if (data.disabled) { + option.disabled = true; + } + + if (data.selected) { + option.selected = true; + } + + if (data.title) { + option.title = data.title; + } + + var $option = $(option); + + var normalizedData = this._normalizeItem(data); + normalizedData.element = option; + + // Override the option's data with the combined data + $.data(option, 'data', normalizedData); + + return $option; + }; + + SelectAdapter.prototype.item = function ($option) { + var data = {}; + + data = $.data($option[0], 'data'); + + if (data != null) { + return data; + } + + if ($option.is('option')) { + data = { + id: $option.val(), + text: $option.text(), + disabled: $option.prop('disabled'), + selected: $option.prop('selected'), + title: $option.prop('title') + }; + } else if ($option.is('optgroup')) { + data = { + text: $option.prop('label'), + children: [], + title: $option.prop('title') + }; + + var $children = $option.children('option'); + var children = []; + + for (var c = 0; c < $children.length; c++) { + var $child = $($children[c]); + + var child = this.item($child); + + children.push(child); + } + + data.children = children; + } + + data = this._normalizeItem(data); + data.element = $option[0]; + + $.data($option[0], 'data', data); + + return data; + }; + + SelectAdapter.prototype._normalizeItem = function (item) { + if (!$.isPlainObject(item)) { + item = { + id: item, + text: item + }; + } + + item = $.extend({}, { + text: '' + }, item); + + var defaults = { + selected: false, + disabled: false + }; + + if (item.id != null) { + item.id = item.id.toString(); + } + + if (item.text != null) { + item.text = item.text.toString(); + } + + if (item._resultId == null && item.id && this.container != null) { + item._resultId = this.generateResultId(this.container, item); + } + + return $.extend({}, defaults, item); + }; + + SelectAdapter.prototype.matches = function (params, data) { + var matcher = this.options.get('matcher'); + + return matcher(params, data); + }; + + return SelectAdapter; +}); + +S2.define('select2/data/array',[ + './select', + '../utils', + 'jquery' +], function (SelectAdapter, Utils, $) { + function ArrayAdapter ($element, options) { + var data = options.get('data') || []; + + ArrayAdapter.__super__.constructor.call(this, $element, options); + + this.addOptions(this.convertToOptions(data)); + } + + Utils.Extend(ArrayAdapter, SelectAdapter); + + ArrayAdapter.prototype.select = function (data) { + var $option = this.$element.find('option').filter(function (i, elm) { + return elm.value == data.id.toString(); + }); + + if ($option.length === 0) { + $option = this.option(data); + + this.addOptions($option); + } + + ArrayAdapter.__super__.select.call(this, data); + }; + + ArrayAdapter.prototype.convertToOptions = function (data) { + var self = this; + + var $existing = this.$element.find('option'); + var existingIds = $existing.map(function () { + return self.item($(this)).id; + }).get(); + + var $options = []; + + // Filter out all items except for the one passed in the argument + function onlyItem (item) { + return function () { + return $(this).val() == item.id; + }; + } + + for (var d = 0; d < data.length; d++) { + var item = this._normalizeItem(data[d]); + + // Skip items which were pre-loaded, only merge the data + if ($.inArray(item.id, existingIds) >= 0) { + var $existingOption = $existing.filter(onlyItem(item)); + + var existingData = this.item($existingOption); + var newData = $.extend(true, {}, item, existingData); + + var $newOption = this.option(newData); + + $existingOption.replaceWith($newOption); + + continue; + } + + var $option = this.option(item); + + if (item.children) { + var $children = this.convertToOptions(item.children); + + Utils.appendMany($option, $children); + } + + $options.push($option); + } + + return $options; + }; + + return ArrayAdapter; +}); + +S2.define('select2/data/ajax',[ + './array', + '../utils', + 'jquery' +], function (ArrayAdapter, Utils, $) { + function AjaxAdapter ($element, options) { + this.ajaxOptions = this._applyDefaults(options.get('ajax')); + + if (this.ajaxOptions.processResults != null) { + this.processResults = this.ajaxOptions.processResults; + } + + AjaxAdapter.__super__.constructor.call(this, $element, options); + } + + Utils.Extend(AjaxAdapter, ArrayAdapter); + + AjaxAdapter.prototype._applyDefaults = function (options) { + var defaults = { + data: function (params) { + return $.extend({}, params, { + q: params.term + }); + }, + transport: function (params, success, failure) { + var $request = $.ajax(params); + + $request.then(success); + $request.fail(failure); + + return $request; + } + }; + + return $.extend({}, defaults, options, true); + }; + + AjaxAdapter.prototype.processResults = function (results) { + return results; + }; + + AjaxAdapter.prototype.query = function (params, callback) { + var matches = []; + var self = this; + + if (this._request != null) { + // JSONP requests cannot always be aborted + if ($.isFunction(this._request.abort)) { + this._request.abort(); + } + + this._request = null; + } + + var options = $.extend({ + type: 'GET' + }, this.ajaxOptions); + + if (typeof options.url === 'function') { + options.url = options.url.call(this.$element, params); + } + + if (typeof options.data === 'function') { + options.data = options.data.call(this.$element, params); + } + + function request () { + var $request = options.transport(options, function (data) { + var results = self.processResults(data, params); + + if (self.options.get('debug') && window.console && console.error) { + // Check to make sure that the response included a `results` key. + if (!results || !results.results || !$.isArray(results.results)) { + console.error( + 'Select2: The AJAX results did not return an array in the ' + + '`results` key of the response.' + ); + } + } + + callback(results); + }, function () { + // Attempt to detect if a request was aborted + // Only works if the transport exposes a status property + if ($request.status && $request.status === '0') { + return; + } + + self.trigger('results:message', { + message: 'errorLoading' + }); + }); + + self._request = $request; + } + + if (this.ajaxOptions.delay && params.term != null) { + if (this._queryTimeout) { + window.clearTimeout(this._queryTimeout); + } + + this._queryTimeout = window.setTimeout(request, this.ajaxOptions.delay); + } else { + request(); + } + }; + + return AjaxAdapter; +}); + +S2.define('select2/data/tags',[ + 'jquery' +], function ($) { + function Tags (decorated, $element, options) { + var tags = options.get('tags'); + + var createTag = options.get('createTag'); + + if (createTag !== undefined) { + this.createTag = createTag; + } + + var insertTag = options.get('insertTag'); + + if (insertTag !== undefined) { + this.insertTag = insertTag; + } + + decorated.call(this, $element, options); + + if ($.isArray(tags)) { + for (var t = 0; t < tags.length; t++) { + var tag = tags[t]; + var item = this._normalizeItem(tag); + + var $option = this.option(item); + + this.$element.append($option); + } + } + } + + Tags.prototype.query = function (decorated, params, callback) { + var self = this; + + this._removeOldTags(); + + if (params.term == null || params.page != null) { + decorated.call(this, params, callback); + return; + } + + function wrapper (obj, child) { + var data = obj.results; + + for (var i = 0; i < data.length; i++) { + var option = data[i]; + + var checkChildren = ( + option.children != null && + !wrapper({ + results: option.children + }, true) + ); + + var checkText = option.text === params.term; + + if (checkText || checkChildren) { + if (child) { + return false; + } + + obj.data = data; + callback(obj); + + return; + } + } + + if (child) { + return true; + } + + var tag = self.createTag(params); + + if (tag != null) { + var $option = self.option(tag); + $option.attr('data-select2-tag', true); + + self.addOptions([$option]); + + self.insertTag(data, tag); + } + + obj.results = data; + + callback(obj); + } + + decorated.call(this, params, wrapper); + }; + + Tags.prototype.createTag = function (decorated, params) { + var term = $.trim(params.term); + + if (term === '') { + return null; + } + + return { + id: term, + text: term + }; + }; + + Tags.prototype.insertTag = function (_, data, tag) { + data.unshift(tag); + }; + + Tags.prototype._removeOldTags = function (_) { + var tag = this._lastTag; + + var $options = this.$element.find('option[data-select2-tag]'); + + $options.each(function () { + if (this.selected) { + return; + } + + $(this).remove(); + }); + }; + + return Tags; +}); + +S2.define('select2/data/tokenizer',[ + 'jquery' +], function ($) { + function Tokenizer (decorated, $element, options) { + var tokenizer = options.get('tokenizer'); + + if (tokenizer !== undefined) { + this.tokenizer = tokenizer; + } + + decorated.call(this, $element, options); + } + + Tokenizer.prototype.bind = function (decorated, container, $container) { + decorated.call(this, container, $container); + + this.$search = container.dropdown.$search || container.selection.$search || + $container.find('.select2-search__field'); + }; + + Tokenizer.prototype.query = function (decorated, params, callback) { + var self = this; + + function createAndSelect (data) { + // Normalize the data object so we can use it for checks + var item = self._normalizeItem(data); + + // Check if the data object already exists as a tag + // Select it if it doesn't + var $existingOptions = self.$element.find('option').filter(function () { + return $(this).val() === item.id; + }); + + // If an existing option wasn't found for it, create the option + if (!$existingOptions.length) { + var $option = self.option(item); + $option.attr('data-select2-tag', true); + + self._removeOldTags(); + self.addOptions([$option]); + } + + // Select the item, now that we know there is an option for it + select(item); + } + + function select (data) { + self.trigger('select', { + data: data + }); + } + + params.term = params.term || ''; + + var tokenData = this.tokenizer(params, this.options, createAndSelect); + + if (tokenData.term !== params.term) { + // Replace the search term if we have the search box + if (this.$search.length) { + this.$search.val(tokenData.term); + this.$search.focus(); + } + + params.term = tokenData.term; + } + + decorated.call(this, params, callback); + }; + + Tokenizer.prototype.tokenizer = function (_, params, options, callback) { + var separators = options.get('tokenSeparators') || []; + var term = params.term; + var i = 0; + + var createTag = this.createTag || function (params) { + return { + id: params.term, + text: params.term + }; + }; + + while (i < term.length) { + var termChar = term[i]; + + if ($.inArray(termChar, separators) === -1) { + i++; + + continue; + } + + var part = term.substr(0, i); + var partParams = $.extend({}, params, { + term: part + }); + + var data = createTag(partParams); + + if (data == null) { + i++; + continue; + } + + callback(data); + + // Reset the term to not include the tokenized portion + term = term.substr(i + 1) || ''; + i = 0; + } + + return { + term: term + }; + }; + + return Tokenizer; +}); + +S2.define('select2/data/minimumInputLength',[ + +], function () { + function MinimumInputLength (decorated, $e, options) { + this.minimumInputLength = options.get('minimumInputLength'); + + decorated.call(this, $e, options); + } + + MinimumInputLength.prototype.query = function (decorated, params, callback) { + params.term = params.term || ''; + + if (params.term.length < this.minimumInputLength) { + this.trigger('results:message', { + message: 'inputTooShort', + args: { + minimum: this.minimumInputLength, + input: params.term, + params: params + } + }); + + return; + } + + decorated.call(this, params, callback); + }; + + return MinimumInputLength; +}); + +S2.define('select2/data/maximumInputLength',[ + +], function () { + function MaximumInputLength (decorated, $e, options) { + this.maximumInputLength = options.get('maximumInputLength'); + + decorated.call(this, $e, options); + } + + MaximumInputLength.prototype.query = function (decorated, params, callback) { + params.term = params.term || ''; + + if (this.maximumInputLength > 0 && + params.term.length > this.maximumInputLength) { + this.trigger('results:message', { + message: 'inputTooLong', + args: { + maximum: this.maximumInputLength, + input: params.term, + params: params + } + }); + + return; + } + + decorated.call(this, params, callback); + }; + + return MaximumInputLength; +}); + +S2.define('select2/data/maximumSelectionLength',[ + +], function (){ + function MaximumSelectionLength (decorated, $e, options) { + this.maximumSelectionLength = options.get('maximumSelectionLength'); + + decorated.call(this, $e, options); + } + + MaximumSelectionLength.prototype.query = + function (decorated, params, callback) { + var self = this; + + this.current(function (currentData) { + var count = currentData != null ? currentData.length : 0; + if (self.maximumSelectionLength > 0 && + count >= self.maximumSelectionLength) { + self.trigger('results:message', { + message: 'maximumSelected', + args: { + maximum: self.maximumSelectionLength + } + }); + return; + } + decorated.call(self, params, callback); + }); + }; + + return MaximumSelectionLength; +}); + +S2.define('select2/dropdown',[ + 'jquery', + './utils' +], function ($, Utils) { + function Dropdown ($element, options) { + this.$element = $element; + this.options = options; + + Dropdown.__super__.constructor.call(this); + } + + Utils.Extend(Dropdown, Utils.Observable); + + Dropdown.prototype.render = function () { + var $dropdown = $( + '' + + '' + + '' + ); + + $dropdown.attr('dir', this.options.get('dir')); + + this.$dropdown = $dropdown; + + return $dropdown; + }; + + Dropdown.prototype.bind = function () { + // Should be implemented in subclasses + }; + + Dropdown.prototype.position = function ($dropdown, $container) { + // Should be implmented in subclasses + }; + + Dropdown.prototype.destroy = function () { + // Remove the dropdown from the DOM + this.$dropdown.remove(); + }; + + return Dropdown; +}); + +S2.define('select2/dropdown/search',[ + 'jquery', + '../utils' +], function ($, Utils) { + function Search () { } + + Search.prototype.render = function (decorated) { + var $rendered = decorated.call(this); + + var $search = $( + '' + + '' + + '' + ); + + this.$searchContainer = $search; + this.$search = $search.find('input'); + + $rendered.prepend($search); + + return $rendered; + }; + + Search.prototype.bind = function (decorated, container, $container) { + var self = this; + + decorated.call(this, container, $container); + + this.$search.on('keydown', function (evt) { + self.trigger('keypress', evt); + + self._keyUpPrevented = evt.isDefaultPrevented(); + }); + + // Workaround for browsers which do not support the `input` event + // This will prevent double-triggering of events for browsers which support + // both the `keyup` and `input` events. + this.$search.on('input', function (evt) { + // Unbind the duplicated `keyup` event + $(this).off('keyup'); + }); + + this.$search.on('keyup input', function (evt) { + self.handleSearch(evt); + }); + + container.on('open', function () { + self.$search.attr('tabindex', 0); + + self.$search.focus(); + + window.setTimeout(function () { + self.$search.focus(); + }, 0); + }); + + container.on('close', function () { + self.$search.attr('tabindex', -1); + + self.$search.val(''); + }); + + container.on('focus', function () { + if (container.isOpen()) { + self.$search.focus(); + } + }); + + container.on('results:all', function (params) { + if (params.query.term == null || params.query.term === '') { + var showSearch = self.showSearch(params); + + if (showSearch) { + self.$searchContainer.removeClass('select2-search--hide'); + } else { + self.$searchContainer.addClass('select2-search--hide'); + } + } + }); + }; + + Search.prototype.handleSearch = function (evt) { + if (!this._keyUpPrevented) { + var input = this.$search.val(); + + this.trigger('query', { + term: input + }); + } + + this._keyUpPrevented = false; + }; + + Search.prototype.showSearch = function (_, params) { + return true; + }; + + return Search; +}); + +S2.define('select2/dropdown/hidePlaceholder',[ + +], function () { + function HidePlaceholder (decorated, $element, options, dataAdapter) { + this.placeholder = this.normalizePlaceholder(options.get('placeholder')); + + decorated.call(this, $element, options, dataAdapter); + } + + HidePlaceholder.prototype.append = function (decorated, data) { + data.results = this.removePlaceholder(data.results); + + decorated.call(this, data); + }; + + HidePlaceholder.prototype.normalizePlaceholder = function (_, placeholder) { + if (typeof placeholder === 'string') { + placeholder = { + id: '', + text: placeholder + }; + } + + return placeholder; + }; + + HidePlaceholder.prototype.removePlaceholder = function (_, data) { + var modifiedData = data.slice(0); + + for (var d = data.length - 1; d >= 0; d--) { + var item = data[d]; + + if (this.placeholder.id === item.id) { + modifiedData.splice(d, 1); + } + } + + return modifiedData; + }; + + return HidePlaceholder; +}); + +S2.define('select2/dropdown/infiniteScroll',[ + 'jquery' +], function ($) { + function InfiniteScroll (decorated, $element, options, dataAdapter) { + this.lastParams = {}; + + decorated.call(this, $element, options, dataAdapter); + + this.$loadingMore = this.createLoadingMore(); + this.loading = false; + } + + InfiniteScroll.prototype.append = function (decorated, data) { + this.$loadingMore.remove(); + this.loading = false; + + decorated.call(this, data); + + if (this.showLoadingMore(data)) { + this.$results.append(this.$loadingMore); + } + }; + + InfiniteScroll.prototype.bind = function (decorated, container, $container) { + var self = this; + + decorated.call(this, container, $container); + + container.on('query', function (params) { + self.lastParams = params; + self.loading = true; + }); + + container.on('query:append', function (params) { + self.lastParams = params; + self.loading = true; + }); + + this.$results.on('scroll', function () { + var isLoadMoreVisible = $.contains( + document.documentElement, + self.$loadingMore[0] + ); + + if (self.loading || !isLoadMoreVisible) { + return; + } + + var currentOffset = self.$results.offset().top + + self.$results.outerHeight(false); + var loadingMoreOffset = self.$loadingMore.offset().top + + self.$loadingMore.outerHeight(false); + + if (currentOffset + 50 >= loadingMoreOffset) { + self.loadMore(); + } + }); + }; + + InfiniteScroll.prototype.loadMore = function () { + this.loading = true; + + var params = $.extend({}, {page: 1}, this.lastParams); + + params.page++; + + this.trigger('query:append', params); + }; + + InfiniteScroll.prototype.showLoadingMore = function (_, data) { + return data.pagination && data.pagination.more; + }; + + InfiniteScroll.prototype.createLoadingMore = function () { + var $option = $( + '
      • ' + ); + + var message = this.options.get('translations').get('loadingMore'); + + $option.html(message(this.lastParams)); + + return $option; + }; + + return InfiniteScroll; +}); + +S2.define('select2/dropdown/attachBody',[ + 'jquery', + '../utils' +], function ($, Utils) { + function AttachBody (decorated, $element, options) { + this.$dropdownParent = options.get('dropdownParent') || $(document.body); + + decorated.call(this, $element, options); + } + + AttachBody.prototype.bind = function (decorated, container, $container) { + var self = this; + + var setupResultsEvents = false; + + decorated.call(this, container, $container); + + container.on('open', function () { + self._showDropdown(); + self._attachPositioningHandler(container); + + if (!setupResultsEvents) { + setupResultsEvents = true; + + container.on('results:all', function () { + self._positionDropdown(); + self._resizeDropdown(); + }); + + container.on('results:append', function () { + self._positionDropdown(); + self._resizeDropdown(); + }); + } + }); + + container.on('close', function () { + self._hideDropdown(); + self._detachPositioningHandler(container); + }); + + this.$dropdownContainer.on('mousedown', function (evt) { + evt.stopPropagation(); + }); + }; + + AttachBody.prototype.destroy = function (decorated) { + decorated.call(this); + + this.$dropdownContainer.remove(); + }; + + AttachBody.prototype.position = function (decorated, $dropdown, $container) { + // Clone all of the container classes + $dropdown.attr('class', $container.attr('class')); + + $dropdown.removeClass('select2'); + $dropdown.addClass('select2-container--open'); + + $dropdown.css({ + position: 'absolute', + top: -999999 + }); + + this.$container = $container; + }; + + AttachBody.prototype.render = function (decorated) { + var $container = $(''); + + var $dropdown = decorated.call(this); + $container.append($dropdown); + + this.$dropdownContainer = $container; + + return $container; + }; + + AttachBody.prototype._hideDropdown = function (decorated) { + this.$dropdownContainer.detach(); + }; + + AttachBody.prototype._attachPositioningHandler = + function (decorated, container) { + var self = this; + + var scrollEvent = 'scroll.select2.' + container.id; + var resizeEvent = 'resize.select2.' + container.id; + var orientationEvent = 'orientationchange.select2.' + container.id; + + var $watchers = this.$container.parents().filter(Utils.hasScroll); + $watchers.each(function () { + $(this).data('select2-scroll-position', { + x: $(this).scrollLeft(), + y: $(this).scrollTop() + }); + }); + + $watchers.on(scrollEvent, function (ev) { + var position = $(this).data('select2-scroll-position'); + $(this).scrollTop(position.y); + }); + + $(window).on(scrollEvent + ' ' + resizeEvent + ' ' + orientationEvent, + function (e) { + self._positionDropdown(); + self._resizeDropdown(); + }); + }; + + AttachBody.prototype._detachPositioningHandler = + function (decorated, container) { + var scrollEvent = 'scroll.select2.' + container.id; + var resizeEvent = 'resize.select2.' + container.id; + var orientationEvent = 'orientationchange.select2.' + container.id; + + var $watchers = this.$container.parents().filter(Utils.hasScroll); + $watchers.off(scrollEvent); + + $(window).off(scrollEvent + ' ' + resizeEvent + ' ' + orientationEvent); + }; + + AttachBody.prototype._positionDropdown = function () { + var $window = $(window); + + var isCurrentlyAbove = this.$dropdown.hasClass('select2-dropdown--above'); + var isCurrentlyBelow = this.$dropdown.hasClass('select2-dropdown--below'); + + var newDirection = null; + + var offset = this.$container.offset(); + + offset.bottom = offset.top + this.$container.outerHeight(false); + + var container = { + height: this.$container.outerHeight(false) + }; + + container.top = offset.top; + container.bottom = offset.top + container.height; + + var dropdown = { + height: this.$dropdown.outerHeight(false) + }; + + var viewport = { + top: $window.scrollTop(), + bottom: $window.scrollTop() + $window.height() + }; + + var enoughRoomAbove = viewport.top < (offset.top - dropdown.height); + var enoughRoomBelow = viewport.bottom > (offset.bottom + dropdown.height); + + var css = { + left: offset.left, + top: container.bottom + }; + + // Determine what the parent element is to use for calciulating the offset + var $offsetParent = this.$dropdownParent; + + // For statically positoned elements, we need to get the element + // that is determining the offset + if ($offsetParent.css('position') === 'static') { + $offsetParent = $offsetParent.offsetParent(); + } + + var parentOffset = $offsetParent.offset(); + + css.top -= parentOffset.top; + css.left -= parentOffset.left; + + if (!isCurrentlyAbove && !isCurrentlyBelow) { + newDirection = 'below'; + } + + if (!enoughRoomBelow && enoughRoomAbove && !isCurrentlyAbove) { + newDirection = 'above'; + } else if (!enoughRoomAbove && enoughRoomBelow && isCurrentlyAbove) { + newDirection = 'below'; + } + + if (newDirection == 'above' || + (isCurrentlyAbove && newDirection !== 'below')) { + css.top = container.top - parentOffset.top - dropdown.height; + } + + if (newDirection != null) { + this.$dropdown + .removeClass('select2-dropdown--below select2-dropdown--above') + .addClass('select2-dropdown--' + newDirection); + this.$container + .removeClass('select2-container--below select2-container--above') + .addClass('select2-container--' + newDirection); + } + + this.$dropdownContainer.css(css); + }; + + AttachBody.prototype._resizeDropdown = function () { + var css = { + width: this.$container.outerWidth(false) + 'px' + }; + + if (this.options.get('dropdownAutoWidth')) { + css.minWidth = css.width; + css.position = 'relative'; + css.width = 'auto'; + } + + this.$dropdown.css(css); + }; + + AttachBody.prototype._showDropdown = function (decorated) { + this.$dropdownContainer.appendTo(this.$dropdownParent); + + this._positionDropdown(); + this._resizeDropdown(); + }; + + return AttachBody; +}); + +S2.define('select2/dropdown/minimumResultsForSearch',[ + +], function () { + function countResults (data) { + var count = 0; + + for (var d = 0; d < data.length; d++) { + var item = data[d]; + + if (item.children) { + count += countResults(item.children); + } else { + count++; + } + } + + return count; + } + + function MinimumResultsForSearch (decorated, $element, options, dataAdapter) { + this.minimumResultsForSearch = options.get('minimumResultsForSearch'); + + if (this.minimumResultsForSearch < 0) { + this.minimumResultsForSearch = Infinity; + } + + decorated.call(this, $element, options, dataAdapter); + } + + MinimumResultsForSearch.prototype.showSearch = function (decorated, params) { + if (countResults(params.data.results) < this.minimumResultsForSearch) { + return false; + } + + return decorated.call(this, params); + }; + + return MinimumResultsForSearch; +}); + +S2.define('select2/dropdown/selectOnClose',[ + +], function () { + function SelectOnClose () { } + + SelectOnClose.prototype.bind = function (decorated, container, $container) { + var self = this; + + decorated.call(this, container, $container); + + container.on('close', function (params) { + self._handleSelectOnClose(params); + }); + }; + + SelectOnClose.prototype._handleSelectOnClose = function (_, params) { + if (params && params.originalSelect2Event != null) { + var event = params.originalSelect2Event; + + // Don't select an item if the close event was triggered from a select or + // unselect event + if (event._type === 'select' || event._type === 'unselect') { + return; + } + } + + var $highlightedResults = this.getHighlightedResults(); + + // Only select highlighted results + if ($highlightedResults.length < 1) { + return; + } + + var data = $highlightedResults.data('data'); + + // Don't re-select already selected resulte + if ( + (data.element != null && data.element.selected) || + (data.element == null && data.selected) + ) { + return; + } + + this.trigger('select', { + data: data + }); + }; + + return SelectOnClose; +}); + +S2.define('select2/dropdown/closeOnSelect',[ + +], function () { + function CloseOnSelect () { } + + CloseOnSelect.prototype.bind = function (decorated, container, $container) { + var self = this; + + decorated.call(this, container, $container); + + container.on('select', function (evt) { + self._selectTriggered(evt); + }); + + container.on('unselect', function (evt) { + self._selectTriggered(evt); + }); + }; + + CloseOnSelect.prototype._selectTriggered = function (_, evt) { + var originalEvent = evt.originalEvent; + + // Don't close if the control key is being held + if (originalEvent && originalEvent.ctrlKey) { + return; + } + + this.trigger('close', { + originalEvent: originalEvent, + originalSelect2Event: evt + }); + }; + + return CloseOnSelect; +}); + +S2.define('select2/i18n/en',[],function () { + // English + return { + errorLoading: function () { + return 'The results could not be loaded.'; + }, + inputTooLong: function (args) { + var overChars = args.input.length - args.maximum; + + var message = 'Please delete ' + overChars + ' character'; + + if (overChars != 1) { + message += 's'; + } + + return message; + }, + inputTooShort: function (args) { + var remainingChars = args.minimum - args.input.length; + + var message = 'Please enter ' + remainingChars + ' or more characters'; + + return message; + }, + loadingMore: function () { + return 'Loading more results…'; + }, + maximumSelected: function (args) { + var message = 'You can only select ' + args.maximum + ' item'; + + if (args.maximum != 1) { + message += 's'; + } + + return message; + }, + noResults: function () { + return 'No results found'; + }, + searching: function () { + return 'Searching…'; + } + }; +}); + +S2.define('select2/defaults',[ + 'jquery', + 'require', + + './results', + + './selection/single', + './selection/multiple', + './selection/placeholder', + './selection/allowClear', + './selection/search', + './selection/eventRelay', + + './utils', + './translation', + './diacritics', + + './data/select', + './data/array', + './data/ajax', + './data/tags', + './data/tokenizer', + './data/minimumInputLength', + './data/maximumInputLength', + './data/maximumSelectionLength', + + './dropdown', + './dropdown/search', + './dropdown/hidePlaceholder', + './dropdown/infiniteScroll', + './dropdown/attachBody', + './dropdown/minimumResultsForSearch', + './dropdown/selectOnClose', + './dropdown/closeOnSelect', + + './i18n/en' +], function ($, require, + + ResultsList, + + SingleSelection, MultipleSelection, Placeholder, AllowClear, + SelectionSearch, EventRelay, + + Utils, Translation, DIACRITICS, + + SelectData, ArrayData, AjaxData, Tags, Tokenizer, + MinimumInputLength, MaximumInputLength, MaximumSelectionLength, + + Dropdown, DropdownSearch, HidePlaceholder, InfiniteScroll, + AttachBody, MinimumResultsForSearch, SelectOnClose, CloseOnSelect, + + EnglishTranslation) { + function Defaults () { + this.reset(); + } + + Defaults.prototype.apply = function (options) { + options = $.extend(true, {}, this.defaults, options); + + if (options.dataAdapter == null) { + if (options.ajax != null) { + options.dataAdapter = AjaxData; + } else if (options.data != null) { + options.dataAdapter = ArrayData; + } else { + options.dataAdapter = SelectData; + } + + if (options.minimumInputLength > 0) { + options.dataAdapter = Utils.Decorate( + options.dataAdapter, + MinimumInputLength + ); + } + + if (options.maximumInputLength > 0) { + options.dataAdapter = Utils.Decorate( + options.dataAdapter, + MaximumInputLength + ); + } + + if (options.maximumSelectionLength > 0) { + options.dataAdapter = Utils.Decorate( + options.dataAdapter, + MaximumSelectionLength + ); + } + + if (options.tags) { + options.dataAdapter = Utils.Decorate(options.dataAdapter, Tags); + } + + if (options.tokenSeparators != null || options.tokenizer != null) { + options.dataAdapter = Utils.Decorate( + options.dataAdapter, + Tokenizer + ); + } + + if (options.query != null) { + var Query = require(options.amdBase + 'compat/query'); + + options.dataAdapter = Utils.Decorate( + options.dataAdapter, + Query + ); + } + + if (options.initSelection != null) { + var InitSelection = require(options.amdBase + 'compat/initSelection'); + + options.dataAdapter = Utils.Decorate( + options.dataAdapter, + InitSelection + ); + } + } + + if (options.resultsAdapter == null) { + options.resultsAdapter = ResultsList; + + if (options.ajax != null) { + options.resultsAdapter = Utils.Decorate( + options.resultsAdapter, + InfiniteScroll + ); + } + + if (options.placeholder != null) { + options.resultsAdapter = Utils.Decorate( + options.resultsAdapter, + HidePlaceholder + ); + } + + if (options.selectOnClose) { + options.resultsAdapter = Utils.Decorate( + options.resultsAdapter, + SelectOnClose + ); + } + } + + if (options.dropdownAdapter == null) { + if (options.multiple) { + options.dropdownAdapter = Dropdown; + } else { + var SearchableDropdown = Utils.Decorate(Dropdown, DropdownSearch); + + options.dropdownAdapter = SearchableDropdown; + } + + if (options.minimumResultsForSearch !== 0) { + options.dropdownAdapter = Utils.Decorate( + options.dropdownAdapter, + MinimumResultsForSearch + ); + } + + if (options.closeOnSelect) { + options.dropdownAdapter = Utils.Decorate( + options.dropdownAdapter, + CloseOnSelect + ); + } + + if ( + options.dropdownCssClass != null || + options.dropdownCss != null || + options.adaptDropdownCssClass != null + ) { + var DropdownCSS = require(options.amdBase + 'compat/dropdownCss'); + + options.dropdownAdapter = Utils.Decorate( + options.dropdownAdapter, + DropdownCSS + ); + } + + options.dropdownAdapter = Utils.Decorate( + options.dropdownAdapter, + AttachBody + ); + } + + if (options.selectionAdapter == null) { + if (options.multiple) { + options.selectionAdapter = MultipleSelection; + } else { + options.selectionAdapter = SingleSelection; + } + + // Add the placeholder mixin if a placeholder was specified + if (options.placeholder != null) { + options.selectionAdapter = Utils.Decorate( + options.selectionAdapter, + Placeholder + ); + } + + if (options.allowClear) { + options.selectionAdapter = Utils.Decorate( + options.selectionAdapter, + AllowClear + ); + } + + if (options.multiple) { + options.selectionAdapter = Utils.Decorate( + options.selectionAdapter, + SelectionSearch + ); + } + + if ( + options.containerCssClass != null || + options.containerCss != null || + options.adaptContainerCssClass != null + ) { + var ContainerCSS = require(options.amdBase + 'compat/containerCss'); + + options.selectionAdapter = Utils.Decorate( + options.selectionAdapter, + ContainerCSS + ); + } + + options.selectionAdapter = Utils.Decorate( + options.selectionAdapter, + EventRelay + ); + } + + if (typeof options.language === 'string') { + // Check if the language is specified with a region + if (options.language.indexOf('-') > 0) { + // Extract the region information if it is included + var languageParts = options.language.split('-'); + var baseLanguage = languageParts[0]; + + options.language = [options.language, baseLanguage]; + } else { + options.language = [options.language]; + } + } + + if ($.isArray(options.language)) { + var languages = new Translation(); + options.language.push('en'); + + var languageNames = options.language; + + for (var l = 0; l < languageNames.length; l++) { + var name = languageNames[l]; + var language = {}; + + try { + // Try to load it with the original name + language = Translation.loadPath(name); + } catch (e) { + try { + // If we couldn't load it, check if it wasn't the full path + name = this.defaults.amdLanguageBase + name; + language = Translation.loadPath(name); + } catch (ex) { + // The translation could not be loaded at all. Sometimes this is + // because of a configuration problem, other times this can be + // because of how Select2 helps load all possible translation files. + if (options.debug && window.console && console.warn) { + console.warn( + 'Select2: The language file for "' + name + '" could not be ' + + 'automatically loaded. A fallback will be used instead.' + ); + } + + continue; + } + } + + languages.extend(language); + } + + options.translations = languages; + } else { + var baseTranslation = Translation.loadPath( + this.defaults.amdLanguageBase + 'en' + ); + var customTranslation = new Translation(options.language); + + customTranslation.extend(baseTranslation); + + options.translations = customTranslation; + } + + return options; + }; + + Defaults.prototype.reset = function () { + function stripDiacritics (text) { + // Used 'uni range + named function' from http://jsperf.com/diacritics/18 + function match(a) { + return DIACRITICS[a] || a; + } + + return text.replace(/[^\u0000-\u007E]/g, match); + } + + function matcher (params, data) { + // Always return the object if there is nothing to compare + if ($.trim(params.term) === '') { + return data; + } + + // Do a recursive check for options with children + if (data.children && data.children.length > 0) { + // Clone the data object if there are children + // This is required as we modify the object to remove any non-matches + var match = $.extend(true, {}, data); + + // Check each child of the option + for (var c = data.children.length - 1; c >= 0; c--) { + var child = data.children[c]; + + var matches = matcher(params, child); + + // If there wasn't a match, remove the object in the array + if (matches == null) { + match.children.splice(c, 1); + } + } + + // If any children matched, return the new object + if (match.children.length > 0) { + return match; + } + + // If there were no matching children, check just the plain object + return matcher(params, match); + } + + var original = stripDiacritics(data.text).toUpperCase(); + var term = stripDiacritics(params.term).toUpperCase(); + + // Check if the text contains the term + if (original.indexOf(term) > -1) { + return data; + } + + // If it doesn't contain the term, don't return anything + return null; + } + + this.defaults = { + amdBase: './', + amdLanguageBase: './i18n/', + closeOnSelect: true, + debug: false, + dropdownAutoWidth: false, + escapeMarkup: Utils.escapeMarkup, + language: EnglishTranslation, + matcher: matcher, + minimumInputLength: 0, + maximumInputLength: 0, + maximumSelectionLength: 0, + minimumResultsForSearch: 0, + selectOnClose: false, + sorter: function (data) { + return data; + }, + templateResult: function (result) { + return result.text; + }, + templateSelection: function (selection) { + return selection.text; + }, + theme: 'default', + width: 'resolve' + }; + }; + + Defaults.prototype.set = function (key, value) { + var camelKey = $.camelCase(key); + + var data = {}; + data[camelKey] = value; + + var convertedData = Utils._convertData(data); + + $.extend(this.defaults, convertedData); + }; + + var defaults = new Defaults(); + + return defaults; +}); + +S2.define('select2/options',[ + 'require', + 'jquery', + './defaults', + './utils' +], function (require, $, Defaults, Utils) { + function Options (options, $element) { + this.options = options; + + if ($element != null) { + this.fromElement($element); + } + + this.options = Defaults.apply(this.options); + + if ($element && $element.is('input')) { + var InputCompat = require(this.get('amdBase') + 'compat/inputData'); + + this.options.dataAdapter = Utils.Decorate( + this.options.dataAdapter, + InputCompat + ); + } + } + + Options.prototype.fromElement = function ($e) { + var excludedData = ['select2']; + + if (this.options.multiple == null) { + this.options.multiple = $e.prop('multiple'); + } + + if (this.options.disabled == null) { + this.options.disabled = $e.prop('disabled'); + } + + if (this.options.language == null) { + if ($e.prop('lang')) { + this.options.language = $e.prop('lang').toLowerCase(); + } else if ($e.closest('[lang]').prop('lang')) { + this.options.language = $e.closest('[lang]').prop('lang'); + } + } + + if (this.options.dir == null) { + if ($e.prop('dir')) { + this.options.dir = $e.prop('dir'); + } else if ($e.closest('[dir]').prop('dir')) { + this.options.dir = $e.closest('[dir]').prop('dir'); + } else { + this.options.dir = 'ltr'; + } + } + + $e.prop('disabled', this.options.disabled); + $e.prop('multiple', this.options.multiple); + + if ($e.data('select2Tags')) { + if (this.options.debug && window.console && console.warn) { + console.warn( + 'Select2: The `data-select2-tags` attribute has been changed to ' + + 'use the `data-data` and `data-tags="true"` attributes and will be ' + + 'removed in future versions of Select2.' + ); + } + + $e.data('data', $e.data('select2Tags')); + $e.data('tags', true); + } + + if ($e.data('ajaxUrl')) { + if (this.options.debug && window.console && console.warn) { + console.warn( + 'Select2: The `data-ajax-url` attribute has been changed to ' + + '`data-ajax--url` and support for the old attribute will be removed' + + ' in future versions of Select2.' + ); + } + + $e.attr('ajax--url', $e.data('ajaxUrl')); + $e.data('ajax--url', $e.data('ajaxUrl')); + } + + var dataset = {}; + + // Prefer the element's `dataset` attribute if it exists + // jQuery 1.x does not correctly handle data attributes with multiple dashes + if ($.fn.jquery && $.fn.jquery.substr(0, 2) == '1.' && $e[0].dataset) { + dataset = $.extend(true, {}, $e[0].dataset, $e.data()); + } else { + dataset = $e.data(); + } + + var data = $.extend(true, {}, dataset); + + data = Utils._convertData(data); + + for (var key in data) { + if ($.inArray(key, excludedData) > -1) { + continue; + } + + if ($.isPlainObject(this.options[key])) { + $.extend(this.options[key], data[key]); + } else { + this.options[key] = data[key]; + } + } + + return this; + }; + + Options.prototype.get = function (key) { + return this.options[key]; + }; + + Options.prototype.set = function (key, val) { + this.options[key] = val; + }; + + return Options; +}); + +S2.define('select2/core',[ + 'jquery', + './options', + './utils', + './keys' +], function ($, Options, Utils, KEYS) { + var Select2 = function ($element, options) { + if ($element.data('select2') != null) { + $element.data('select2').destroy(); + } + + this.$element = $element; + + this.id = this._generateId($element); + + options = options || {}; + + this.options = new Options(options, $element); + + Select2.__super__.constructor.call(this); + + // Set up the tabindex + + var tabindex = $element.attr('tabindex') || 0; + $element.data('old-tabindex', tabindex); + $element.attr('tabindex', '-1'); + + // Set up containers and adapters + + var DataAdapter = this.options.get('dataAdapter'); + this.dataAdapter = new DataAdapter($element, this.options); + + var $container = this.render(); + + this._placeContainer($container); + + var SelectionAdapter = this.options.get('selectionAdapter'); + this.selection = new SelectionAdapter($element, this.options); + this.$selection = this.selection.render(); + + this.selection.position(this.$selection, $container); + + var DropdownAdapter = this.options.get('dropdownAdapter'); + this.dropdown = new DropdownAdapter($element, this.options); + this.$dropdown = this.dropdown.render(); + + this.dropdown.position(this.$dropdown, $container); + + var ResultsAdapter = this.options.get('resultsAdapter'); + this.results = new ResultsAdapter($element, this.options, this.dataAdapter); + this.$results = this.results.render(); + + this.results.position(this.$results, this.$dropdown); + + // Bind events + + var self = this; + + // Bind the container to all of the adapters + this._bindAdapters(); + + // Register any DOM event handlers + this._registerDomEvents(); + + // Register any internal event handlers + this._registerDataEvents(); + this._registerSelectionEvents(); + this._registerDropdownEvents(); + this._registerResultsEvents(); + this._registerEvents(); + + // Set the initial state + this.dataAdapter.current(function (initialData) { + self.trigger('selection:update', { + data: initialData + }); + }); + + // Hide the original select + $element.addClass('select2-hidden-accessible'); + $element.attr('aria-hidden', 'true'); + + // Synchronize any monitored attributes + this._syncAttributes(); + + $element.data('select2', this); + }; + + Utils.Extend(Select2, Utils.Observable); + + Select2.prototype._generateId = function ($element) { + var id = ''; + + if ($element.attr('id') != null) { + id = $element.attr('id'); + } else if ($element.attr('name') != null) { + id = $element.attr('name') + '-' + Utils.generateChars(2); + } else { + id = Utils.generateChars(4); + } + + id = id.replace(/(:|\.|\[|\]|,)/g, ''); + id = 'select2-' + id; + + return id; + }; + + Select2.prototype._placeContainer = function ($container) { + $container.insertAfter(this.$element); + + var width = this._resolveWidth(this.$element, this.options.get('width')); + + if (width != null) { + $container.css('width', width); + } + }; + + Select2.prototype._resolveWidth = function ($element, method) { + var WIDTH = /^width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/i; + + if (method == 'resolve') { + var styleWidth = this._resolveWidth($element, 'style'); + + if (styleWidth != null) { + return styleWidth; + } + + return this._resolveWidth($element, 'element'); + } + + if (method == 'element') { + var elementWidth = $element.outerWidth(false); + + if (elementWidth <= 0) { + return 'auto'; + } + + return elementWidth + 'px'; + } + + if (method == 'style') { + var style = $element.attr('style'); + + if (typeof(style) !== 'string') { + return null; + } + + var attrs = style.split(';'); + + for (var i = 0, l = attrs.length; i < l; i = i + 1) { + var attr = attrs[i].replace(/\s/g, ''); + var matches = attr.match(WIDTH); + + if (matches !== null && matches.length >= 1) { + return matches[1]; + } + } + + return null; + } + + return method; + }; + + Select2.prototype._bindAdapters = function () { + this.dataAdapter.bind(this, this.$container); + this.selection.bind(this, this.$container); + + this.dropdown.bind(this, this.$container); + this.results.bind(this, this.$container); + }; + + Select2.prototype._registerDomEvents = function () { + var self = this; + + this.$element.on('change.select2', function () { + self.dataAdapter.current(function (data) { + self.trigger('selection:update', { + data: data + }); + }); + }); + + this.$element.on('focus.select2', function (evt) { + self.trigger('focus', evt); + }); + + this._syncA = Utils.bind(this._syncAttributes, this); + this._syncS = Utils.bind(this._syncSubtree, this); + + if (this.$element[0].attachEvent) { + this.$element[0].attachEvent('onpropertychange', this._syncA); + } + + var observer = window.MutationObserver || + window.WebKitMutationObserver || + window.MozMutationObserver + ; + + if (observer != null) { + this._observer = new observer(function (mutations) { + $.each(mutations, self._syncA); + $.each(mutations, self._syncS); + }); + this._observer.observe(this.$element[0], { + attributes: true, + childList: true, + subtree: false + }); + } else if (this.$element[0].addEventListener) { + this.$element[0].addEventListener( + 'DOMAttrModified', + self._syncA, + false + ); + this.$element[0].addEventListener( + 'DOMNodeInserted', + self._syncS, + false + ); + this.$element[0].addEventListener( + 'DOMNodeRemoved', + self._syncS, + false + ); + } + }; + + Select2.prototype._registerDataEvents = function () { + var self = this; + + this.dataAdapter.on('*', function (name, params) { + self.trigger(name, params); + }); + }; + + Select2.prototype._registerSelectionEvents = function () { + var self = this; + var nonRelayEvents = ['toggle', 'focus']; + + this.selection.on('toggle', function () { + self.toggleDropdown(); + }); + + this.selection.on('focus', function (params) { + self.focus(params); + }); + + this.selection.on('*', function (name, params) { + if ($.inArray(name, nonRelayEvents) !== -1) { + return; + } + + self.trigger(name, params); + }); + }; + + Select2.prototype._registerDropdownEvents = function () { + var self = this; + + this.dropdown.on('*', function (name, params) { + self.trigger(name, params); + }); + }; + + Select2.prototype._registerResultsEvents = function () { + var self = this; + + this.results.on('*', function (name, params) { + self.trigger(name, params); + }); + }; + + Select2.prototype._registerEvents = function () { + var self = this; + + this.on('open', function () { + self.$container.addClass('select2-container--open'); + }); + + this.on('close', function () { + self.$container.removeClass('select2-container--open'); + }); + + this.on('enable', function () { + self.$container.removeClass('select2-container--disabled'); + }); + + this.on('disable', function () { + self.$container.addClass('select2-container--disabled'); + }); + + this.on('blur', function () { + self.$container.removeClass('select2-container--focus'); + }); + + this.on('query', function (params) { + if (!self.isOpen()) { + self.trigger('open', {}); + } + + this.dataAdapter.query(params, function (data) { + self.trigger('results:all', { + data: data, + query: params + }); + }); + }); + + this.on('query:append', function (params) { + this.dataAdapter.query(params, function (data) { + self.trigger('results:append', { + data: data, + query: params + }); + }); + }); + + this.on('keypress', function (evt) { + var key = evt.which; + + if (self.isOpen()) { + if (key === KEYS.ESC || key === KEYS.TAB || + (key === KEYS.UP && evt.altKey)) { + self.close(); + + evt.preventDefault(); + } else if (key === KEYS.ENTER) { + self.trigger('results:select', {}); + + evt.preventDefault(); + } else if ((key === KEYS.SPACE && evt.ctrlKey)) { + self.trigger('results:toggle', {}); + + evt.preventDefault(); + } else if (key === KEYS.UP) { + self.trigger('results:previous', {}); + + evt.preventDefault(); + } else if (key === KEYS.DOWN) { + self.trigger('results:next', {}); + + evt.preventDefault(); + } + } else { + if (key === KEYS.ENTER || key === KEYS.SPACE || + (key === KEYS.DOWN && evt.altKey)) { + self.open(); + + evt.preventDefault(); + } + } + }); + }; + + Select2.prototype._syncAttributes = function () { + this.options.set('disabled', this.$element.prop('disabled')); + + if (this.options.get('disabled')) { + if (this.isOpen()) { + this.close(); + } + + this.trigger('disable', {}); + } else { + this.trigger('enable', {}); + } + }; + + Select2.prototype._syncSubtree = function (evt, mutations) { + var changed = false; + var self = this; + + // Ignore any mutation events raised for elements that aren't options or + // optgroups. This handles the case when the select element is destroyed + if ( + evt && evt.target && ( + evt.target.nodeName !== 'OPTION' && evt.target.nodeName !== 'OPTGROUP' + ) + ) { + return; + } + + if (!mutations) { + // If mutation events aren't supported, then we can only assume that the + // change affected the selections + changed = true; + } else if (mutations.addedNodes && mutations.addedNodes.length > 0) { + for (var n = 0; n < mutations.addedNodes.length; n++) { + var node = mutations.addedNodes[n]; + + if (node.selected) { + changed = true; + } + } + } else if (mutations.removedNodes && mutations.removedNodes.length > 0) { + changed = true; + } + + // Only re-pull the data if we think there is a change + if (changed) { + this.dataAdapter.current(function (currentData) { + self.trigger('selection:update', { + data: currentData + }); + }); + } + }; + + /** + * Override the trigger method to automatically trigger pre-events when + * there are events that can be prevented. + */ + Select2.prototype.trigger = function (name, args) { + var actualTrigger = Select2.__super__.trigger; + var preTriggerMap = { + 'open': 'opening', + 'close': 'closing', + 'select': 'selecting', + 'unselect': 'unselecting' + }; + + if (args === undefined) { + args = {}; + } + + if (name in preTriggerMap) { + var preTriggerName = preTriggerMap[name]; + var preTriggerArgs = { + prevented: false, + name: name, + args: args + }; + + actualTrigger.call(this, preTriggerName, preTriggerArgs); + + if (preTriggerArgs.prevented) { + args.prevented = true; + + return; + } + } + + actualTrigger.call(this, name, args); + }; + + Select2.prototype.toggleDropdown = function () { + if (this.options.get('disabled')) { + return; + } + + if (this.isOpen()) { + this.close(); + } else { + this.open(); + } + }; + + Select2.prototype.open = function () { + if (this.isOpen()) { + return; + } + + this.trigger('query', {}); + }; + + Select2.prototype.close = function () { + if (!this.isOpen()) { + return; + } + + this.trigger('close', {}); + }; + + Select2.prototype.isOpen = function () { + return this.$container.hasClass('select2-container--open'); + }; + + Select2.prototype.hasFocus = function () { + return this.$container.hasClass('select2-container--focus'); + }; + + Select2.prototype.focus = function (data) { + // No need to re-trigger focus events if we are already focused + if (this.hasFocus()) { + return; + } + + this.$container.addClass('select2-container--focus'); + this.trigger('focus', {}); + }; + + Select2.prototype.enable = function (args) { + if (this.options.get('debug') && window.console && console.warn) { + console.warn( + 'Select2: The `select2("enable")` method has been deprecated and will' + + ' be removed in later Select2 versions. Use $element.prop("disabled")' + + ' instead.' + ); + } + + if (args == null || args.length === 0) { + args = [true]; + } + + var disabled = !args[0]; + + this.$element.prop('disabled', disabled); + }; + + Select2.prototype.data = function () { + if (this.options.get('debug') && + arguments.length > 0 && window.console && console.warn) { + console.warn( + 'Select2: Data can no longer be set using `select2("data")`. You ' + + 'should consider setting the value instead using `$element.val()`.' + ); + } + + var data = []; + + this.dataAdapter.current(function (currentData) { + data = currentData; + }); + + return data; + }; + + Select2.prototype.val = function (args) { + if (this.options.get('debug') && window.console && console.warn) { + console.warn( + 'Select2: The `select2("val")` method has been deprecated and will be' + + ' removed in later Select2 versions. Use $element.val() instead.' + ); + } + + if (args == null || args.length === 0) { + return this.$element.val(); + } + + var newVal = args[0]; + + if ($.isArray(newVal)) { + newVal = $.map(newVal, function (obj) { + return obj.toString(); + }); + } + + this.$element.val(newVal).trigger('change'); + }; + + Select2.prototype.destroy = function () { + this.$container.remove(); + + if (this.$element[0].detachEvent) { + this.$element[0].detachEvent('onpropertychange', this._syncA); + } + + if (this._observer != null) { + this._observer.disconnect(); + this._observer = null; + } else if (this.$element[0].removeEventListener) { + this.$element[0] + .removeEventListener('DOMAttrModified', this._syncA, false); + this.$element[0] + .removeEventListener('DOMNodeInserted', this._syncS, false); + this.$element[0] + .removeEventListener('DOMNodeRemoved', this._syncS, false); + } + + this._syncA = null; + this._syncS = null; + + this.$element.off('.select2'); + this.$element.attr('tabindex', this.$element.data('old-tabindex')); + + this.$element.removeClass('select2-hidden-accessible'); + this.$element.attr('aria-hidden', 'false'); + this.$element.removeData('select2'); + + this.dataAdapter.destroy(); + this.selection.destroy(); + this.dropdown.destroy(); + this.results.destroy(); + + this.dataAdapter = null; + this.selection = null; + this.dropdown = null; + this.results = null; + }; + + Select2.prototype.render = function () { + var $container = $( + '' + + '' + + '' + + '' + ); + + $container.attr('dir', this.options.get('dir')); + + this.$container = $container; + + this.$container.addClass('select2-container--' + this.options.get('theme')); + + $container.data('element', this.$element); + + return $container; + }; + + return Select2; +}); + +S2.define('select2/compat/utils',[ + 'jquery' +], function ($) { + function syncCssClasses ($dest, $src, adapter) { + var classes, replacements = [], adapted; + + classes = $.trim($dest.attr('class')); + + if (classes) { + classes = '' + classes; // for IE which returns object + + $(classes.split(/\s+/)).each(function () { + // Save all Select2 classes + if (this.indexOf('select2-') === 0) { + replacements.push(this); + } + }); + } + + classes = $.trim($src.attr('class')); + + if (classes) { + classes = '' + classes; // for IE which returns object + + $(classes.split(/\s+/)).each(function () { + // Only adapt non-Select2 classes + if (this.indexOf('select2-') !== 0) { + adapted = adapter(this); + + if (adapted != null) { + replacements.push(adapted); + } + } + }); + } + + $dest.attr('class', replacements.join(' ')); + } + + return { + syncCssClasses: syncCssClasses + }; +}); + +S2.define('select2/compat/containerCss',[ + 'jquery', + './utils' +], function ($, CompatUtils) { + // No-op CSS adapter that discards all classes by default + function _containerAdapter (clazz) { + return null; + } + + function ContainerCSS () { } + + ContainerCSS.prototype.render = function (decorated) { + var $container = decorated.call(this); + + var containerCssClass = this.options.get('containerCssClass') || ''; + + if ($.isFunction(containerCssClass)) { + containerCssClass = containerCssClass(this.$element); + } + + var containerCssAdapter = this.options.get('adaptContainerCssClass'); + containerCssAdapter = containerCssAdapter || _containerAdapter; + + if (containerCssClass.indexOf(':all:') !== -1) { + containerCssClass = containerCssClass.replace(':all:', ''); + + var _cssAdapter = containerCssAdapter; + + containerCssAdapter = function (clazz) { + var adapted = _cssAdapter(clazz); + + if (adapted != null) { + // Append the old one along with the adapted one + return adapted + ' ' + clazz; + } + + return clazz; + }; + } + + var containerCss = this.options.get('containerCss') || {}; + + if ($.isFunction(containerCss)) { + containerCss = containerCss(this.$element); + } + + CompatUtils.syncCssClasses($container, this.$element, containerCssAdapter); + + $container.css(containerCss); + $container.addClass(containerCssClass); + + return $container; + }; + + return ContainerCSS; +}); + +S2.define('select2/compat/dropdownCss',[ + 'jquery', + './utils' +], function ($, CompatUtils) { + // No-op CSS adapter that discards all classes by default + function _dropdownAdapter (clazz) { + return null; + } + + function DropdownCSS () { } + + DropdownCSS.prototype.render = function (decorated) { + var $dropdown = decorated.call(this); + + var dropdownCssClass = this.options.get('dropdownCssClass') || ''; + + if ($.isFunction(dropdownCssClass)) { + dropdownCssClass = dropdownCssClass(this.$element); + } + + var dropdownCssAdapter = this.options.get('adaptDropdownCssClass'); + dropdownCssAdapter = dropdownCssAdapter || _dropdownAdapter; + + if (dropdownCssClass.indexOf(':all:') !== -1) { + dropdownCssClass = dropdownCssClass.replace(':all:', ''); + + var _cssAdapter = dropdownCssAdapter; + + dropdownCssAdapter = function (clazz) { + var adapted = _cssAdapter(clazz); + + if (adapted != null) { + // Append the old one along with the adapted one + return adapted + ' ' + clazz; + } + + return clazz; + }; + } + + var dropdownCss = this.options.get('dropdownCss') || {}; + + if ($.isFunction(dropdownCss)) { + dropdownCss = dropdownCss(this.$element); + } + + CompatUtils.syncCssClasses($dropdown, this.$element, dropdownCssAdapter); + + $dropdown.css(dropdownCss); + $dropdown.addClass(dropdownCssClass); + + return $dropdown; + }; + + return DropdownCSS; +}); + +S2.define('select2/compat/initSelection',[ + 'jquery' +], function ($) { + function InitSelection (decorated, $element, options) { + if (options.get('debug') && window.console && console.warn) { + console.warn( + 'Select2: The `initSelection` option has been deprecated in favor' + + ' of a custom data adapter that overrides the `current` method. ' + + 'This method is now called multiple times instead of a single ' + + 'time when the instance is initialized. Support will be removed ' + + 'for the `initSelection` option in future versions of Select2' + ); + } + + this.initSelection = options.get('initSelection'); + this._isInitialized = false; + + decorated.call(this, $element, options); + } + + InitSelection.prototype.current = function (decorated, callback) { + var self = this; + + if (this._isInitialized) { + decorated.call(this, callback); + + return; + } + + this.initSelection.call(null, this.$element, function (data) { + self._isInitialized = true; + + if (!$.isArray(data)) { + data = [data]; + } + + callback(data); + }); + }; + + return InitSelection; +}); + +S2.define('select2/compat/inputData',[ + 'jquery' +], function ($) { + function InputData (decorated, $element, options) { + this._currentData = []; + this._valueSeparator = options.get('valueSeparator') || ','; + + if ($element.prop('type') === 'hidden') { + if (options.get('debug') && console && console.warn) { + console.warn( + 'Select2: Using a hidden input with Select2 is no longer ' + + 'supported and may stop working in the future. It is recommended ' + + 'to use a `');this.$searchContainer=c,this.$search=c.find("input");var d=b.call(this);return this._transferTabIndex(),d},d.prototype.bind=function(a,b,d){var e=this;a.call(this,b,d),b.on("open",function(){e.$search.trigger("focus")}),b.on("close",function(){e.$search.val(""),e.$search.removeAttr("aria-activedescendant"),e.$search.trigger("focus")}),b.on("enable",function(){e.$search.prop("disabled",!1),e._transferTabIndex()}),b.on("disable",function(){e.$search.prop("disabled",!0)}),b.on("focus",function(a){e.$search.trigger("focus")}),b.on("results:focus",function(a){e.$search.attr("aria-activedescendant",a.id)}),this.$selection.on("focusin",".select2-search--inline",function(a){e.trigger("focus",a)}),this.$selection.on("focusout",".select2-search--inline",function(a){e._handleBlur(a)}),this.$selection.on("keydown",".select2-search--inline",function(a){a.stopPropagation(),e.trigger("keypress",a),e._keyUpPrevented=a.isDefaultPrevented();var b=a.which;if(b===c.BACKSPACE&&""===e.$search.val()){var d=e.$searchContainer.prev(".select2-selection__choice");if(d.length>0){var f=d.data("data");e.searchRemoveChoice(f),a.preventDefault()}}});var f=document.documentMode,g=f&&11>=f;this.$selection.on("input.searchcheck",".select2-search--inline",function(a){return g?void e.$selection.off("input.search input.searchcheck"):void e.$selection.off("keyup.search")}),this.$selection.on("keyup.search input.search",".select2-search--inline",function(a){if(g&&"input"===a.type)return void e.$selection.off("input.search input.searchcheck");var b=a.which;b!=c.SHIFT&&b!=c.CTRL&&b!=c.ALT&&b!=c.TAB&&e.handleSearch(a)})},d.prototype._transferTabIndex=function(a){this.$search.attr("tabindex",this.$selection.attr("tabindex")),this.$selection.attr("tabindex","-1")},d.prototype.createPlaceholder=function(a,b){this.$search.attr("placeholder",b.text)},d.prototype.update=function(a,b){var c=this.$search[0]==document.activeElement;this.$search.attr("placeholder",""),a.call(this,b),this.$selection.find(".select2-selection__rendered").append(this.$searchContainer),this.resizeSearch(),c&&this.$search.focus()},d.prototype.handleSearch=function(){if(this.resizeSearch(),!this._keyUpPrevented){var a=this.$search.val();this.trigger("query",{term:a})}this._keyUpPrevented=!1},d.prototype.searchRemoveChoice=function(a,b){this.trigger("unselect",{data:b}),this.$search.val(b.text),this.handleSearch()},d.prototype.resizeSearch=function(){this.$search.css("width","25px");var a="";if(""!==this.$search.attr("placeholder"))a=this.$selection.find(".select2-selection__rendered").innerWidth();else{var b=this.$search.val().length+1;a=.75*b+"em"}this.$search.css("width",a)},d}),b.define("select2/selection/eventRelay",["jquery"],function(a){function b(){}return b.prototype.bind=function(b,c,d){var e=this,f=["open","opening","close","closing","select","selecting","unselect","unselecting"],g=["opening","closing","selecting","unselecting"];b.call(this,c,d),c.on("*",function(b,c){if(-1!==a.inArray(b,f)){c=c||{};var d=a.Event("select2:"+b,{params:c});e.$element.trigger(d),-1!==a.inArray(b,g)&&(c.prevented=d.isDefaultPrevented())}})},b}),b.define("select2/translation",["jquery","require"],function(a,b){function c(a){this.dict=a||{}}return c.prototype.all=function(){return this.dict},c.prototype.get=function(a){return this.dict[a]},c.prototype.extend=function(b){this.dict=a.extend({},b.all(),this.dict)},c._cache={},c.loadPath=function(a){if(!(a in c._cache)){var d=b(a);c._cache[a]=d}return new c(c._cache[a])},c}),b.define("select2/diacritics",[],function(){var a={"Ⓐ":"A","A":"A","À":"A","Á":"A","Â":"A","Ầ":"A","Ấ":"A","Ẫ":"A","Ẩ":"A","Ã":"A","Ā":"A","Ă":"A","Ằ":"A","Ắ":"A","Ẵ":"A","Ẳ":"A","Ȧ":"A","Ǡ":"A","Ä":"A","Ǟ":"A","Ả":"A","Å":"A","Ǻ":"A","Ǎ":"A","Ȁ":"A","Ȃ":"A","Ạ":"A","Ậ":"A","Ặ":"A","Ḁ":"A","Ą":"A","Ⱥ":"A","Ɐ":"A","Ꜳ":"AA","Æ":"AE","Ǽ":"AE","Ǣ":"AE","Ꜵ":"AO","Ꜷ":"AU","Ꜹ":"AV","Ꜻ":"AV","Ꜽ":"AY","Ⓑ":"B","B":"B","Ḃ":"B","Ḅ":"B","Ḇ":"B","Ƀ":"B","Ƃ":"B","Ɓ":"B","Ⓒ":"C","C":"C","Ć":"C","Ĉ":"C","Ċ":"C","Č":"C","Ç":"C","Ḉ":"C","Ƈ":"C","Ȼ":"C","Ꜿ":"C","Ⓓ":"D","D":"D","Ḋ":"D","Ď":"D","Ḍ":"D","Ḑ":"D","Ḓ":"D","Ḏ":"D","Đ":"D","Ƌ":"D","Ɗ":"D","Ɖ":"D","Ꝺ":"D","DZ":"DZ","DŽ":"DZ","Dz":"Dz","Dž":"Dz","Ⓔ":"E","E":"E","È":"E","É":"E","Ê":"E","Ề":"E","Ế":"E","Ễ":"E","Ể":"E","Ẽ":"E","Ē":"E","Ḕ":"E","Ḗ":"E","Ĕ":"E","Ė":"E","Ë":"E","Ẻ":"E","Ě":"E","Ȅ":"E","Ȇ":"E","Ẹ":"E","Ệ":"E","Ȩ":"E","Ḝ":"E","Ę":"E","Ḙ":"E","Ḛ":"E","Ɛ":"E","Ǝ":"E","Ⓕ":"F","F":"F","Ḟ":"F","Ƒ":"F","Ꝼ":"F","Ⓖ":"G","G":"G","Ǵ":"G","Ĝ":"G","Ḡ":"G","Ğ":"G","Ġ":"G","Ǧ":"G","Ģ":"G","Ǥ":"G","Ɠ":"G","Ꞡ":"G","Ᵹ":"G","Ꝿ":"G","Ⓗ":"H","H":"H","Ĥ":"H","Ḣ":"H","Ḧ":"H","Ȟ":"H","Ḥ":"H","Ḩ":"H","Ḫ":"H","Ħ":"H","Ⱨ":"H","Ⱶ":"H","Ɥ":"H","Ⓘ":"I","I":"I","Ì":"I","Í":"I","Î":"I","Ĩ":"I","Ī":"I","Ĭ":"I","İ":"I","Ï":"I","Ḯ":"I","Ỉ":"I","Ǐ":"I","Ȉ":"I","Ȋ":"I","Ị":"I","Į":"I","Ḭ":"I","Ɨ":"I","Ⓙ":"J","J":"J","Ĵ":"J","Ɉ":"J","Ⓚ":"K","K":"K","Ḱ":"K","Ǩ":"K","Ḳ":"K","Ķ":"K","Ḵ":"K","Ƙ":"K","Ⱪ":"K","Ꝁ":"K","Ꝃ":"K","Ꝅ":"K","Ꞣ":"K","Ⓛ":"L","L":"L","Ŀ":"L","Ĺ":"L","Ľ":"L","Ḷ":"L","Ḹ":"L","Ļ":"L","Ḽ":"L","Ḻ":"L","Ł":"L","Ƚ":"L","Ɫ":"L","Ⱡ":"L","Ꝉ":"L","Ꝇ":"L","Ꞁ":"L","LJ":"LJ","Lj":"Lj","Ⓜ":"M","M":"M","Ḿ":"M","Ṁ":"M","Ṃ":"M","Ɱ":"M","Ɯ":"M","Ⓝ":"N","N":"N","Ǹ":"N","Ń":"N","Ñ":"N","Ṅ":"N","Ň":"N","Ṇ":"N","Ņ":"N","Ṋ":"N","Ṉ":"N","Ƞ":"N","Ɲ":"N","Ꞑ":"N","Ꞥ":"N","NJ":"NJ","Nj":"Nj","Ⓞ":"O","O":"O","Ò":"O","Ó":"O","Ô":"O","Ồ":"O","Ố":"O","Ỗ":"O","Ổ":"O","Õ":"O","Ṍ":"O","Ȭ":"O","Ṏ":"O","Ō":"O","Ṑ":"O","Ṓ":"O","Ŏ":"O","Ȯ":"O","Ȱ":"O","Ö":"O","Ȫ":"O","Ỏ":"O","Ő":"O","Ǒ":"O","Ȍ":"O","Ȏ":"O","Ơ":"O","Ờ":"O","Ớ":"O","Ỡ":"O","Ở":"O","Ợ":"O","Ọ":"O","Ộ":"O","Ǫ":"O","Ǭ":"O","Ø":"O","Ǿ":"O","Ɔ":"O","Ɵ":"O","Ꝋ":"O","Ꝍ":"O","Ƣ":"OI","Ꝏ":"OO","Ȣ":"OU","Ⓟ":"P","P":"P","Ṕ":"P","Ṗ":"P","Ƥ":"P","Ᵽ":"P","Ꝑ":"P","Ꝓ":"P","Ꝕ":"P","Ⓠ":"Q","Q":"Q","Ꝗ":"Q","Ꝙ":"Q","Ɋ":"Q","Ⓡ":"R","R":"R","Ŕ":"R","Ṙ":"R","Ř":"R","Ȑ":"R","Ȓ":"R","Ṛ":"R","Ṝ":"R","Ŗ":"R","Ṟ":"R","Ɍ":"R","Ɽ":"R","Ꝛ":"R","Ꞧ":"R","Ꞃ":"R","Ⓢ":"S","S":"S","ẞ":"S","Ś":"S","Ṥ":"S","Ŝ":"S","Ṡ":"S","Š":"S","Ṧ":"S","Ṣ":"S","Ṩ":"S","Ș":"S","Ş":"S","Ȿ":"S","Ꞩ":"S","Ꞅ":"S","Ⓣ":"T","T":"T","Ṫ":"T","Ť":"T","Ṭ":"T","Ț":"T","Ţ":"T","Ṱ":"T","Ṯ":"T","Ŧ":"T","Ƭ":"T","Ʈ":"T","Ⱦ":"T","Ꞇ":"T","Ꜩ":"TZ","Ⓤ":"U","U":"U","Ù":"U","Ú":"U","Û":"U","Ũ":"U","Ṹ":"U","Ū":"U","Ṻ":"U","Ŭ":"U","Ü":"U","Ǜ":"U","Ǘ":"U","Ǖ":"U","Ǚ":"U","Ủ":"U","Ů":"U","Ű":"U","Ǔ":"U","Ȕ":"U","Ȗ":"U","Ư":"U","Ừ":"U","Ứ":"U","Ữ":"U","Ử":"U","Ự":"U","Ụ":"U","Ṳ":"U","Ų":"U","Ṷ":"U","Ṵ":"U","Ʉ":"U","Ⓥ":"V","V":"V","Ṽ":"V","Ṿ":"V","Ʋ":"V","Ꝟ":"V","Ʌ":"V","Ꝡ":"VY","Ⓦ":"W","W":"W","Ẁ":"W","Ẃ":"W","Ŵ":"W","Ẇ":"W","Ẅ":"W","Ẉ":"W","Ⱳ":"W","Ⓧ":"X","X":"X","Ẋ":"X","Ẍ":"X","Ⓨ":"Y","Y":"Y","Ỳ":"Y","Ý":"Y","Ŷ":"Y","Ỹ":"Y","Ȳ":"Y","Ẏ":"Y","Ÿ":"Y","Ỷ":"Y","Ỵ":"Y","Ƴ":"Y","Ɏ":"Y","Ỿ":"Y","Ⓩ":"Z","Z":"Z","Ź":"Z","Ẑ":"Z","Ż":"Z","Ž":"Z","Ẓ":"Z","Ẕ":"Z","Ƶ":"Z","Ȥ":"Z","Ɀ":"Z","Ⱬ":"Z","Ꝣ":"Z","ⓐ":"a","a":"a","ẚ":"a","à":"a","á":"a","â":"a","ầ":"a","ấ":"a","ẫ":"a","ẩ":"a","ã":"a","ā":"a","ă":"a","ằ":"a","ắ":"a","ẵ":"a","ẳ":"a","ȧ":"a","ǡ":"a","ä":"a","ǟ":"a","ả":"a","å":"a","ǻ":"a","ǎ":"a","ȁ":"a","ȃ":"a","ạ":"a","ậ":"a","ặ":"a","ḁ":"a","ą":"a","ⱥ":"a","ɐ":"a","ꜳ":"aa","æ":"ae","ǽ":"ae","ǣ":"ae","ꜵ":"ao","ꜷ":"au","ꜹ":"av","ꜻ":"av","ꜽ":"ay","ⓑ":"b","b":"b","ḃ":"b","ḅ":"b","ḇ":"b","ƀ":"b","ƃ":"b","ɓ":"b","ⓒ":"c","c":"c","ć":"c","ĉ":"c","ċ":"c","č":"c","ç":"c","ḉ":"c","ƈ":"c","ȼ":"c","ꜿ":"c","ↄ":"c","ⓓ":"d","d":"d","ḋ":"d","ď":"d","ḍ":"d","ḑ":"d","ḓ":"d","ḏ":"d","đ":"d","ƌ":"d","ɖ":"d","ɗ":"d","ꝺ":"d","dz":"dz","dž":"dz","ⓔ":"e","e":"e","è":"e","é":"e","ê":"e","ề":"e","ế":"e","ễ":"e","ể":"e","ẽ":"e","ē":"e","ḕ":"e","ḗ":"e","ĕ":"e","ė":"e","ë":"e","ẻ":"e","ě":"e","ȅ":"e","ȇ":"e","ẹ":"e","ệ":"e","ȩ":"e","ḝ":"e","ę":"e","ḙ":"e","ḛ":"e","ɇ":"e","ɛ":"e","ǝ":"e","ⓕ":"f","f":"f","ḟ":"f","ƒ":"f","ꝼ":"f","ⓖ":"g","g":"g","ǵ":"g","ĝ":"g","ḡ":"g","ğ":"g","ġ":"g","ǧ":"g","ģ":"g","ǥ":"g","ɠ":"g","ꞡ":"g","ᵹ":"g","ꝿ":"g","ⓗ":"h","h":"h","ĥ":"h","ḣ":"h","ḧ":"h","ȟ":"h","ḥ":"h","ḩ":"h","ḫ":"h","ẖ":"h","ħ":"h","ⱨ":"h","ⱶ":"h","ɥ":"h","ƕ":"hv","ⓘ":"i","i":"i","ì":"i","í":"i","î":"i","ĩ":"i","ī":"i","ĭ":"i","ï":"i","ḯ":"i","ỉ":"i","ǐ":"i","ȉ":"i","ȋ":"i","ị":"i","į":"i","ḭ":"i","ɨ":"i","ı":"i","ⓙ":"j","j":"j","ĵ":"j","ǰ":"j","ɉ":"j","ⓚ":"k","k":"k","ḱ":"k","ǩ":"k","ḳ":"k","ķ":"k","ḵ":"k","ƙ":"k","ⱪ":"k","ꝁ":"k","ꝃ":"k","ꝅ":"k","ꞣ":"k","ⓛ":"l","l":"l","ŀ":"l","ĺ":"l","ľ":"l","ḷ":"l","ḹ":"l","ļ":"l","ḽ":"l","ḻ":"l","ſ":"l","ł":"l","ƚ":"l","ɫ":"l","ⱡ":"l","ꝉ":"l","ꞁ":"l","ꝇ":"l","lj":"lj","ⓜ":"m","m":"m","ḿ":"m","ṁ":"m","ṃ":"m","ɱ":"m","ɯ":"m","ⓝ":"n","n":"n","ǹ":"n","ń":"n","ñ":"n","ṅ":"n","ň":"n","ṇ":"n","ņ":"n","ṋ":"n","ṉ":"n","ƞ":"n","ɲ":"n","ʼn":"n","ꞑ":"n","ꞥ":"n","nj":"nj","ⓞ":"o","o":"o","ò":"o","ó":"o","ô":"o","ồ":"o","ố":"o","ỗ":"o","ổ":"o","õ":"o","ṍ":"o","ȭ":"o","ṏ":"o","ō":"o","ṑ":"o","ṓ":"o","ŏ":"o","ȯ":"o","ȱ":"o","ö":"o","ȫ":"o","ỏ":"o","ő":"o","ǒ":"o","ȍ":"o","ȏ":"o","ơ":"o","ờ":"o","ớ":"o","ỡ":"o","ở":"o","ợ":"o","ọ":"o","ộ":"o","ǫ":"o","ǭ":"o","ø":"o","ǿ":"o","ɔ":"o","ꝋ":"o","ꝍ":"o","ɵ":"o","ƣ":"oi","ȣ":"ou","ꝏ":"oo","ⓟ":"p","p":"p","ṕ":"p","ṗ":"p","ƥ":"p","ᵽ":"p","ꝑ":"p","ꝓ":"p","ꝕ":"p","ⓠ":"q","q":"q","ɋ":"q","ꝗ":"q","ꝙ":"q","ⓡ":"r","r":"r","ŕ":"r","ṙ":"r","ř":"r","ȑ":"r","ȓ":"r","ṛ":"r","ṝ":"r","ŗ":"r","ṟ":"r","ɍ":"r","ɽ":"r","ꝛ":"r","ꞧ":"r","ꞃ":"r","ⓢ":"s","s":"s","ß":"s","ś":"s","ṥ":"s","ŝ":"s","ṡ":"s","š":"s","ṧ":"s","ṣ":"s","ṩ":"s","ș":"s","ş":"s","ȿ":"s","ꞩ":"s","ꞅ":"s","ẛ":"s","ⓣ":"t","t":"t","ṫ":"t","ẗ":"t","ť":"t","ṭ":"t","ț":"t","ţ":"t","ṱ":"t","ṯ":"t","ŧ":"t","ƭ":"t","ʈ":"t","ⱦ":"t","ꞇ":"t","ꜩ":"tz","ⓤ":"u","u":"u","ù":"u","ú":"u","û":"u","ũ":"u","ṹ":"u","ū":"u","ṻ":"u","ŭ":"u","ü":"u","ǜ":"u","ǘ":"u","ǖ":"u","ǚ":"u","ủ":"u","ů":"u","ű":"u","ǔ":"u","ȕ":"u","ȗ":"u","ư":"u","ừ":"u","ứ":"u","ữ":"u","ử":"u","ự":"u","ụ":"u","ṳ":"u","ų":"u","ṷ":"u","ṵ":"u","ʉ":"u","ⓥ":"v","v":"v","ṽ":"v","ṿ":"v","ʋ":"v","ꝟ":"v","ʌ":"v","ꝡ":"vy","ⓦ":"w","w":"w","ẁ":"w","ẃ":"w","ŵ":"w","ẇ":"w","ẅ":"w","ẘ":"w","ẉ":"w","ⱳ":"w","ⓧ":"x","x":"x","ẋ":"x","ẍ":"x","ⓨ":"y","y":"y","ỳ":"y","ý":"y","ŷ":"y","ỹ":"y","ȳ":"y","ẏ":"y","ÿ":"y","ỷ":"y","ẙ":"y","ỵ":"y","ƴ":"y","ɏ":"y","ỿ":"y","ⓩ":"z","z":"z","ź":"z","ẑ":"z","ż":"z","ž":"z","ẓ":"z","ẕ":"z","ƶ":"z","ȥ":"z","ɀ":"z","ⱬ":"z","ꝣ":"z","Ά":"Α","Έ":"Ε","Ή":"Η","Ί":"Ι","Ϊ":"Ι","Ό":"Ο","Ύ":"Υ","Ϋ":"Υ","Ώ":"Ω","ά":"α","έ":"ε","ή":"η","ί":"ι","ϊ":"ι","ΐ":"ι","ό":"ο","ύ":"υ","ϋ":"υ","ΰ":"υ","ω":"ω","ς":"σ"};return a}),b.define("select2/data/base",["../utils"],function(a){function b(a,c){b.__super__.constructor.call(this)}return a.Extend(b,a.Observable),b.prototype.current=function(a){throw new Error("The `current` method must be defined in child classes.")},b.prototype.query=function(a,b){throw new Error("The `query` method must be defined in child classes.")},b.prototype.bind=function(a,b){},b.prototype.destroy=function(){},b.prototype.generateResultId=function(b,c){var d=b.id+"-result-";return d+=a.generateChars(4),d+=null!=c.id?"-"+c.id.toString():"-"+a.generateChars(4)},b}),b.define("select2/data/select",["./base","../utils","jquery"],function(a,b,c){function d(a,b){this.$element=a,this.options=b,d.__super__.constructor.call(this)}return b.Extend(d,a),d.prototype.current=function(a){var b=[],d=this;this.$element.find(":selected").each(function(){var a=c(this),e=d.item(a);b.push(e)}),a(b)},d.prototype.select=function(a){var b=this;if(a.selected=!0,c(a.element).is("option"))return a.element.selected=!0,void this.$element.trigger("change"); +if(this.$element.prop("multiple"))this.current(function(d){var e=[];a=[a],a.push.apply(a,d);for(var f=0;f=0){var k=f.filter(d(j)),l=this.item(k),m=c.extend(!0,{},j,l),n=this.option(m);k.replaceWith(n)}else{var o=this.option(j);if(j.children){var p=this.convertToOptions(j.children);b.appendMany(o,p)}h.push(o)}}return h},d}),b.define("select2/data/ajax",["./array","../utils","jquery"],function(a,b,c){function d(a,b){this.ajaxOptions=this._applyDefaults(b.get("ajax")),null!=this.ajaxOptions.processResults&&(this.processResults=this.ajaxOptions.processResults),d.__super__.constructor.call(this,a,b)}return b.Extend(d,a),d.prototype._applyDefaults=function(a){var b={data:function(a){return c.extend({},a,{q:a.term})},transport:function(a,b,d){var e=c.ajax(a);return e.then(b),e.fail(d),e}};return c.extend({},b,a,!0)},d.prototype.processResults=function(a){return a},d.prototype.query=function(a,b){function d(){var d=f.transport(f,function(d){var f=e.processResults(d,a);e.options.get("debug")&&window.console&&console.error&&(f&&f.results&&c.isArray(f.results)||console.error("Select2: The AJAX results did not return an array in the `results` key of the response.")),b(f)},function(){d.status&&"0"===d.status||e.trigger("results:message",{message:"errorLoading"})});e._request=d}var e=this;null!=this._request&&(c.isFunction(this._request.abort)&&this._request.abort(),this._request=null);var f=c.extend({type:"GET"},this.ajaxOptions);"function"==typeof f.url&&(f.url=f.url.call(this.$element,a)),"function"==typeof f.data&&(f.data=f.data.call(this.$element,a)),this.ajaxOptions.delay&&null!=a.term?(this._queryTimeout&&window.clearTimeout(this._queryTimeout),this._queryTimeout=window.setTimeout(d,this.ajaxOptions.delay)):d()},d}),b.define("select2/data/tags",["jquery"],function(a){function b(b,c,d){var e=d.get("tags"),f=d.get("createTag");void 0!==f&&(this.createTag=f);var g=d.get("insertTag");if(void 0!==g&&(this.insertTag=g),b.call(this,c,d),a.isArray(e))for(var h=0;h0&&b.term.length>this.maximumInputLength?void this.trigger("results:message",{message:"inputTooLong",args:{maximum:this.maximumInputLength,input:b.term,params:b}}):void a.call(this,b,c)},a}),b.define("select2/data/maximumSelectionLength",[],function(){function a(a,b,c){this.maximumSelectionLength=c.get("maximumSelectionLength"),a.call(this,b,c)}return a.prototype.query=function(a,b,c){var d=this;this.current(function(e){var f=null!=e?e.length:0;return d.maximumSelectionLength>0&&f>=d.maximumSelectionLength?void d.trigger("results:message",{message:"maximumSelected",args:{maximum:d.maximumSelectionLength}}):void a.call(d,b,c)})},a}),b.define("select2/dropdown",["jquery","./utils"],function(a,b){function c(a,b){this.$element=a,this.options=b,c.__super__.constructor.call(this)}return b.Extend(c,b.Observable),c.prototype.render=function(){var b=a('');return b.attr("dir",this.options.get("dir")),this.$dropdown=b,b},c.prototype.bind=function(){},c.prototype.position=function(a,b){},c.prototype.destroy=function(){this.$dropdown.remove()},c}),b.define("select2/dropdown/search",["jquery","../utils"],function(a,b){function c(){}return c.prototype.render=function(b){var c=b.call(this),d=a('');return this.$searchContainer=d,this.$search=d.find("input"),c.prepend(d),c},c.prototype.bind=function(b,c,d){var e=this;b.call(this,c,d),this.$search.on("keydown",function(a){e.trigger("keypress",a),e._keyUpPrevented=a.isDefaultPrevented()}),this.$search.on("input",function(b){a(this).off("keyup")}),this.$search.on("keyup input",function(a){e.handleSearch(a)}),c.on("open",function(){e.$search.attr("tabindex",0),e.$search.focus(),window.setTimeout(function(){e.$search.focus()},0)}),c.on("close",function(){e.$search.attr("tabindex",-1),e.$search.val("")}),c.on("focus",function(){c.isOpen()&&e.$search.focus()}),c.on("results:all",function(a){if(null==a.query.term||""===a.query.term){var b=e.showSearch(a);b?e.$searchContainer.removeClass("select2-search--hide"):e.$searchContainer.addClass("select2-search--hide")}})},c.prototype.handleSearch=function(a){if(!this._keyUpPrevented){var b=this.$search.val();this.trigger("query",{term:b})}this._keyUpPrevented=!1},c.prototype.showSearch=function(a,b){return!0},c}),b.define("select2/dropdown/hidePlaceholder",[],function(){function a(a,b,c,d){this.placeholder=this.normalizePlaceholder(c.get("placeholder")),a.call(this,b,c,d)}return a.prototype.append=function(a,b){b.results=this.removePlaceholder(b.results),a.call(this,b)},a.prototype.normalizePlaceholder=function(a,b){return"string"==typeof b&&(b={id:"",text:b}),b},a.prototype.removePlaceholder=function(a,b){for(var c=b.slice(0),d=b.length-1;d>=0;d--){var e=b[d];this.placeholder.id===e.id&&c.splice(d,1)}return c},a}),b.define("select2/dropdown/infiniteScroll",["jquery"],function(a){function b(a,b,c,d){this.lastParams={},a.call(this,b,c,d),this.$loadingMore=this.createLoadingMore(),this.loading=!1}return b.prototype.append=function(a,b){this.$loadingMore.remove(),this.loading=!1,a.call(this,b),this.showLoadingMore(b)&&this.$results.append(this.$loadingMore)},b.prototype.bind=function(b,c,d){var e=this;b.call(this,c,d),c.on("query",function(a){e.lastParams=a,e.loading=!0}),c.on("query:append",function(a){e.lastParams=a,e.loading=!0}),this.$results.on("scroll",function(){var b=a.contains(document.documentElement,e.$loadingMore[0]);if(!e.loading&&b){var c=e.$results.offset().top+e.$results.outerHeight(!1),d=e.$loadingMore.offset().top+e.$loadingMore.outerHeight(!1);c+50>=d&&e.loadMore()}})},b.prototype.loadMore=function(){this.loading=!0;var b=a.extend({},{page:1},this.lastParams);b.page++,this.trigger("query:append",b)},b.prototype.showLoadingMore=function(a,b){return b.pagination&&b.pagination.more},b.prototype.createLoadingMore=function(){var b=a('
      • '),c=this.options.get("translations").get("loadingMore");return b.html(c(this.lastParams)),b},b}),b.define("select2/dropdown/attachBody",["jquery","../utils"],function(a,b){function c(b,c,d){this.$dropdownParent=d.get("dropdownParent")||a(document.body),b.call(this,c,d)}return c.prototype.bind=function(a,b,c){var d=this,e=!1;a.call(this,b,c),b.on("open",function(){d._showDropdown(),d._attachPositioningHandler(b),e||(e=!0,b.on("results:all",function(){d._positionDropdown(),d._resizeDropdown()}),b.on("results:append",function(){d._positionDropdown(),d._resizeDropdown()}))}),b.on("close",function(){d._hideDropdown(),d._detachPositioningHandler(b)}),this.$dropdownContainer.on("mousedown",function(a){a.stopPropagation()})},c.prototype.destroy=function(a){a.call(this),this.$dropdownContainer.remove()},c.prototype.position=function(a,b,c){b.attr("class",c.attr("class")),b.removeClass("select2"),b.addClass("select2-container--open"),b.css({position:"absolute",top:-999999}),this.$container=c},c.prototype.render=function(b){var c=a(""),d=b.call(this);return c.append(d),this.$dropdownContainer=c,c},c.prototype._hideDropdown=function(a){this.$dropdownContainer.detach()},c.prototype._attachPositioningHandler=function(c,d){var e=this,f="scroll.select2."+d.id,g="resize.select2."+d.id,h="orientationchange.select2."+d.id,i=this.$container.parents().filter(b.hasScroll);i.each(function(){a(this).data("select2-scroll-position",{x:a(this).scrollLeft(),y:a(this).scrollTop()})}),i.on(f,function(b){var c=a(this).data("select2-scroll-position");a(this).scrollTop(c.y)}),a(window).on(f+" "+g+" "+h,function(a){e._positionDropdown(),e._resizeDropdown()})},c.prototype._detachPositioningHandler=function(c,d){var e="scroll.select2."+d.id,f="resize.select2."+d.id,g="orientationchange.select2."+d.id,h=this.$container.parents().filter(b.hasScroll);h.off(e),a(window).off(e+" "+f+" "+g)},c.prototype._positionDropdown=function(){var b=a(window),c=this.$dropdown.hasClass("select2-dropdown--above"),d=this.$dropdown.hasClass("select2-dropdown--below"),e=null,f=this.$container.offset();f.bottom=f.top+this.$container.outerHeight(!1);var g={height:this.$container.outerHeight(!1)};g.top=f.top,g.bottom=f.top+g.height;var h={height:this.$dropdown.outerHeight(!1)},i={top:b.scrollTop(),bottom:b.scrollTop()+b.height()},j=i.topf.bottom+h.height,l={left:f.left,top:g.bottom},m=this.$dropdownParent;"static"===m.css("position")&&(m=m.offsetParent());var n=m.offset();l.top-=n.top,l.left-=n.left,c||d||(e="below"),k||!j||c?!j&&k&&c&&(e="below"):e="above",("above"==e||c&&"below"!==e)&&(l.top=g.top-n.top-h.height),null!=e&&(this.$dropdown.removeClass("select2-dropdown--below select2-dropdown--above").addClass("select2-dropdown--"+e),this.$container.removeClass("select2-container--below select2-container--above").addClass("select2-container--"+e)),this.$dropdownContainer.css(l)},c.prototype._resizeDropdown=function(){var a={width:this.$container.outerWidth(!1)+"px"};this.options.get("dropdownAutoWidth")&&(a.minWidth=a.width,a.position="relative",a.width="auto"),this.$dropdown.css(a)},c.prototype._showDropdown=function(a){this.$dropdownContainer.appendTo(this.$dropdownParent),this._positionDropdown(),this._resizeDropdown()},c}),b.define("select2/dropdown/minimumResultsForSearch",[],function(){function a(b){for(var c=0,d=0;d0&&(l.dataAdapter=j.Decorate(l.dataAdapter,r)),l.maximumInputLength>0&&(l.dataAdapter=j.Decorate(l.dataAdapter,s)),l.maximumSelectionLength>0&&(l.dataAdapter=j.Decorate(l.dataAdapter,t)),l.tags&&(l.dataAdapter=j.Decorate(l.dataAdapter,p)),(null!=l.tokenSeparators||null!=l.tokenizer)&&(l.dataAdapter=j.Decorate(l.dataAdapter,q)),null!=l.query){var C=b(l.amdBase+"compat/query");l.dataAdapter=j.Decorate(l.dataAdapter,C)}if(null!=l.initSelection){var D=b(l.amdBase+"compat/initSelection");l.dataAdapter=j.Decorate(l.dataAdapter,D)}}if(null==l.resultsAdapter&&(l.resultsAdapter=c,null!=l.ajax&&(l.resultsAdapter=j.Decorate(l.resultsAdapter,x)),null!=l.placeholder&&(l.resultsAdapter=j.Decorate(l.resultsAdapter,w)),l.selectOnClose&&(l.resultsAdapter=j.Decorate(l.resultsAdapter,A))),null==l.dropdownAdapter){if(l.multiple)l.dropdownAdapter=u;else{var E=j.Decorate(u,v);l.dropdownAdapter=E}if(0!==l.minimumResultsForSearch&&(l.dropdownAdapter=j.Decorate(l.dropdownAdapter,z)),l.closeOnSelect&&(l.dropdownAdapter=j.Decorate(l.dropdownAdapter,B)),null!=l.dropdownCssClass||null!=l.dropdownCss||null!=l.adaptDropdownCssClass){var F=b(l.amdBase+"compat/dropdownCss");l.dropdownAdapter=j.Decorate(l.dropdownAdapter,F)}l.dropdownAdapter=j.Decorate(l.dropdownAdapter,y)}if(null==l.selectionAdapter){if(l.multiple?l.selectionAdapter=e:l.selectionAdapter=d,null!=l.placeholder&&(l.selectionAdapter=j.Decorate(l.selectionAdapter,f)),l.allowClear&&(l.selectionAdapter=j.Decorate(l.selectionAdapter,g)),l.multiple&&(l.selectionAdapter=j.Decorate(l.selectionAdapter,h)),null!=l.containerCssClass||null!=l.containerCss||null!=l.adaptContainerCssClass){var G=b(l.amdBase+"compat/containerCss");l.selectionAdapter=j.Decorate(l.selectionAdapter,G)}l.selectionAdapter=j.Decorate(l.selectionAdapter,i)}if("string"==typeof l.language)if(l.language.indexOf("-")>0){var H=l.language.split("-"),I=H[0];l.language=[l.language,I]}else l.language=[l.language];if(a.isArray(l.language)){var J=new k;l.language.push("en");for(var K=l.language,L=0;L0){for(var f=a.extend(!0,{},e),g=e.children.length-1;g>=0;g--){var h=e.children[g],i=c(d,h);null==i&&f.children.splice(g,1)}return f.children.length>0?f:c(d,f)}var j=b(e.text).toUpperCase(),k=b(d.term).toUpperCase();return j.indexOf(k)>-1?e:null}this.defaults={amdBase:"./",amdLanguageBase:"./i18n/",closeOnSelect:!0,debug:!1,dropdownAutoWidth:!1,escapeMarkup:j.escapeMarkup,language:C,matcher:c,minimumInputLength:0,maximumInputLength:0,maximumSelectionLength:0,minimumResultsForSearch:0,selectOnClose:!1,sorter:function(a){return a},templateResult:function(a){return a.text},templateSelection:function(a){return a.text},theme:"default",width:"resolve"}},D.prototype.set=function(b,c){var d=a.camelCase(b),e={};e[d]=c;var f=j._convertData(e);a.extend(this.defaults,f)};var E=new D;return E}),b.define("select2/options",["require","jquery","./defaults","./utils"],function(a,b,c,d){function e(b,e){if(this.options=b,null!=e&&this.fromElement(e),this.options=c.apply(this.options),e&&e.is("input")){var f=a(this.get("amdBase")+"compat/inputData");this.options.dataAdapter=d.Decorate(this.options.dataAdapter,f)}}return e.prototype.fromElement=function(a){var c=["select2"];null==this.options.multiple&&(this.options.multiple=a.prop("multiple")),null==this.options.disabled&&(this.options.disabled=a.prop("disabled")),null==this.options.language&&(a.prop("lang")?this.options.language=a.prop("lang").toLowerCase():a.closest("[lang]").prop("lang")&&(this.options.language=a.closest("[lang]").prop("lang"))),null==this.options.dir&&(a.prop("dir")?this.options.dir=a.prop("dir"):a.closest("[dir]").prop("dir")?this.options.dir=a.closest("[dir]").prop("dir"):this.options.dir="ltr"),a.prop("disabled",this.options.disabled),a.prop("multiple",this.options.multiple),a.data("select2Tags")&&(this.options.debug&&window.console&&console.warn&&console.warn('Select2: The `data-select2-tags` attribute has been changed to use the `data-data` and `data-tags="true"` attributes and will be removed in future versions of Select2.'),a.data("data",a.data("select2Tags")),a.data("tags",!0)),a.data("ajaxUrl")&&(this.options.debug&&window.console&&console.warn&&console.warn("Select2: The `data-ajax-url` attribute has been changed to `data-ajax--url` and support for the old attribute will be removed in future versions of Select2."),a.attr("ajax--url",a.data("ajaxUrl")),a.data("ajax--url",a.data("ajaxUrl")));var e={};e=b.fn.jquery&&"1."==b.fn.jquery.substr(0,2)&&a[0].dataset?b.extend(!0,{},a[0].dataset,a.data()):a.data();var f=b.extend(!0,{},e);f=d._convertData(f);for(var g in f)b.inArray(g,c)>-1||(b.isPlainObject(this.options[g])?b.extend(this.options[g],f[g]):this.options[g]=f[g]);return this},e.prototype.get=function(a){return this.options[a]},e.prototype.set=function(a,b){this.options[a]=b},e}),b.define("select2/core",["jquery","./options","./utils","./keys"],function(a,b,c,d){var e=function(a,c){null!=a.data("select2")&&a.data("select2").destroy(),this.$element=a,this.id=this._generateId(a),c=c||{},this.options=new b(c,a),e.__super__.constructor.call(this);var d=a.attr("tabindex")||0;a.data("old-tabindex",d),a.attr("tabindex","-1");var f=this.options.get("dataAdapter");this.dataAdapter=new f(a,this.options);var g=this.render();this._placeContainer(g);var h=this.options.get("selectionAdapter");this.selection=new h(a,this.options),this.$selection=this.selection.render(),this.selection.position(this.$selection,g);var i=this.options.get("dropdownAdapter");this.dropdown=new i(a,this.options),this.$dropdown=this.dropdown.render(),this.dropdown.position(this.$dropdown,g);var j=this.options.get("resultsAdapter");this.results=new j(a,this.options,this.dataAdapter),this.$results=this.results.render(),this.results.position(this.$results,this.$dropdown);var k=this;this._bindAdapters(),this._registerDomEvents(),this._registerDataEvents(),this._registerSelectionEvents(),this._registerDropdownEvents(),this._registerResultsEvents(),this._registerEvents(),this.dataAdapter.current(function(a){k.trigger("selection:update",{data:a})}),a.addClass("select2-hidden-accessible"),a.attr("aria-hidden","true"),this._syncAttributes(),a.data("select2",this)};return c.Extend(e,c.Observable),e.prototype._generateId=function(a){var b="";return b=null!=a.attr("id")?a.attr("id"):null!=a.attr("name")?a.attr("name")+"-"+c.generateChars(2):c.generateChars(4),b=b.replace(/(:|\.|\[|\]|,)/g,""),b="select2-"+b},e.prototype._placeContainer=function(a){a.insertAfter(this.$element);var b=this._resolveWidth(this.$element,this.options.get("width"));null!=b&&a.css("width",b)},e.prototype._resolveWidth=function(a,b){var c=/^width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/i;if("resolve"==b){var d=this._resolveWidth(a,"style");return null!=d?d:this._resolveWidth(a,"element")}if("element"==b){var e=a.outerWidth(!1);return 0>=e?"auto":e+"px"}if("style"==b){var f=a.attr("style");if("string"!=typeof f)return null;for(var g=f.split(";"),h=0,i=g.length;i>h;h+=1){var j=g[h].replace(/\s/g,""),k=j.match(c);if(null!==k&&k.length>=1)return k[1]}return null}return b},e.prototype._bindAdapters=function(){this.dataAdapter.bind(this,this.$container),this.selection.bind(this,this.$container),this.dropdown.bind(this,this.$container),this.results.bind(this,this.$container)},e.prototype._registerDomEvents=function(){var b=this;this.$element.on("change.select2",function(){b.dataAdapter.current(function(a){b.trigger("selection:update",{data:a})})}),this.$element.on("focus.select2",function(a){b.trigger("focus",a)}),this._syncA=c.bind(this._syncAttributes,this),this._syncS=c.bind(this._syncSubtree,this),this.$element[0].attachEvent&&this.$element[0].attachEvent("onpropertychange",this._syncA);var d=window.MutationObserver||window.WebKitMutationObserver||window.MozMutationObserver;null!=d?(this._observer=new d(function(c){a.each(c,b._syncA),a.each(c,b._syncS)}),this._observer.observe(this.$element[0],{attributes:!0,childList:!0,subtree:!1})):this.$element[0].addEventListener&&(this.$element[0].addEventListener("DOMAttrModified",b._syncA,!1),this.$element[0].addEventListener("DOMNodeInserted",b._syncS,!1),this.$element[0].addEventListener("DOMNodeRemoved",b._syncS,!1))},e.prototype._registerDataEvents=function(){var a=this;this.dataAdapter.on("*",function(b,c){a.trigger(b,c)})},e.prototype._registerSelectionEvents=function(){var b=this,c=["toggle","focus"];this.selection.on("toggle",function(){b.toggleDropdown()}),this.selection.on("focus",function(a){b.focus(a)}),this.selection.on("*",function(d,e){-1===a.inArray(d,c)&&b.trigger(d,e)})},e.prototype._registerDropdownEvents=function(){var a=this;this.dropdown.on("*",function(b,c){a.trigger(b,c)})},e.prototype._registerResultsEvents=function(){var a=this;this.results.on("*",function(b,c){a.trigger(b,c)})},e.prototype._registerEvents=function(){var a=this;this.on("open",function(){a.$container.addClass("select2-container--open")}),this.on("close",function(){a.$container.removeClass("select2-container--open")}),this.on("enable",function(){a.$container.removeClass("select2-container--disabled")}),this.on("disable",function(){a.$container.addClass("select2-container--disabled")}),this.on("blur",function(){a.$container.removeClass("select2-container--focus")}),this.on("query",function(b){a.isOpen()||a.trigger("open",{}),this.dataAdapter.query(b,function(c){a.trigger("results:all",{data:c,query:b})})}),this.on("query:append",function(b){this.dataAdapter.query(b,function(c){a.trigger("results:append",{data:c,query:b})})}),this.on("keypress",function(b){var c=b.which;a.isOpen()?c===d.ESC||c===d.TAB||c===d.UP&&b.altKey?(a.close(),b.preventDefault()):c===d.ENTER?(a.trigger("results:select",{}),b.preventDefault()):c===d.SPACE&&b.ctrlKey?(a.trigger("results:toggle",{}),b.preventDefault()):c===d.UP?(a.trigger("results:previous",{}),b.preventDefault()):c===d.DOWN&&(a.trigger("results:next",{}),b.preventDefault()):(c===d.ENTER||c===d.SPACE||c===d.DOWN&&b.altKey)&&(a.open(),b.preventDefault())})},e.prototype._syncAttributes=function(){this.options.set("disabled",this.$element.prop("disabled")),this.options.get("disabled")?(this.isOpen()&&this.close(),this.trigger("disable",{})):this.trigger("enable",{})},e.prototype._syncSubtree=function(a,b){var c=!1,d=this;if(!a||!a.target||"OPTION"===a.target.nodeName||"OPTGROUP"===a.target.nodeName){if(b)if(b.addedNodes&&b.addedNodes.length>0)for(var e=0;e0&&(c=!0);else c=!0;c&&this.dataAdapter.current(function(a){d.trigger("selection:update",{data:a})})}},e.prototype.trigger=function(a,b){var c=e.__super__.trigger,d={open:"opening",close:"closing",select:"selecting",unselect:"unselecting"};if(void 0===b&&(b={}),a in d){var f=d[a],g={prevented:!1,name:a,args:b};if(c.call(this,f,g),g.prevented)return void(b.prevented=!0)}c.call(this,a,b)},e.prototype.toggleDropdown=function(){this.options.get("disabled")||(this.isOpen()?this.close():this.open())},e.prototype.open=function(){this.isOpen()||this.trigger("query",{})},e.prototype.close=function(){this.isOpen()&&this.trigger("close",{})},e.prototype.isOpen=function(){return this.$container.hasClass("select2-container--open")},e.prototype.hasFocus=function(){return this.$container.hasClass("select2-container--focus")},e.prototype.focus=function(a){this.hasFocus()||(this.$container.addClass("select2-container--focus"),this.trigger("focus",{}))},e.prototype.enable=function(a){this.options.get("debug")&&window.console&&console.warn&&console.warn('Select2: The `select2("enable")` method has been deprecated and will be removed in later Select2 versions. Use $element.prop("disabled") instead.'),(null==a||0===a.length)&&(a=[!0]);var b=!a[0];this.$element.prop("disabled",b)},e.prototype.data=function(){this.options.get("debug")&&arguments.length>0&&window.console&&console.warn&&console.warn('Select2: Data can no longer be set using `select2("data")`. You should consider setting the value instead using `$element.val()`.');var a=[];return this.dataAdapter.current(function(b){a=b}),a},e.prototype.val=function(b){if(this.options.get("debug")&&window.console&&console.warn&&console.warn('Select2: The `select2("val")` method has been deprecated and will be removed in later Select2 versions. Use $element.val() instead.'),null==b||0===b.length)return this.$element.val();var c=b[0];a.isArray(c)&&(c=a.map(c,function(a){return a.toString()})),this.$element.val(c).trigger("change")},e.prototype.destroy=function(){this.$container.remove(),this.$element[0].detachEvent&&this.$element[0].detachEvent("onpropertychange",this._syncA),null!=this._observer?(this._observer.disconnect(),this._observer=null):this.$element[0].removeEventListener&&(this.$element[0].removeEventListener("DOMAttrModified",this._syncA,!1),this.$element[0].removeEventListener("DOMNodeInserted",this._syncS,!1),this.$element[0].removeEventListener("DOMNodeRemoved",this._syncS,!1)),this._syncA=null,this._syncS=null,this.$element.off(".select2"),this.$element.attr("tabindex",this.$element.data("old-tabindex")),this.$element.removeClass("select2-hidden-accessible"),this.$element.attr("aria-hidden","false"),this.$element.removeData("select2"),this.dataAdapter.destroy(),this.selection.destroy(),this.dropdown.destroy(),this.results.destroy(),this.dataAdapter=null,this.selection=null,this.dropdown=null,this.results=null; +},e.prototype.render=function(){var b=a('');return b.attr("dir",this.options.get("dir")),this.$container=b,this.$container.addClass("select2-container--"+this.options.get("theme")),b.data("element",this.$element),b},e}),b.define("select2/compat/utils",["jquery"],function(a){function b(b,c,d){var e,f,g=[];e=a.trim(b.attr("class")),e&&(e=""+e,a(e.split(/\s+/)).each(function(){0===this.indexOf("select2-")&&g.push(this)})),e=a.trim(c.attr("class")),e&&(e=""+e,a(e.split(/\s+/)).each(function(){0!==this.indexOf("select2-")&&(f=d(this),null!=f&&g.push(f))})),b.attr("class",g.join(" "))}return{syncCssClasses:b}}),b.define("select2/compat/containerCss",["jquery","./utils"],function(a,b){function c(a){return null}function d(){}return d.prototype.render=function(d){var e=d.call(this),f=this.options.get("containerCssClass")||"";a.isFunction(f)&&(f=f(this.$element));var g=this.options.get("adaptContainerCssClass");if(g=g||c,-1!==f.indexOf(":all:")){f=f.replace(":all:","");var h=g;g=function(a){var b=h(a);return null!=b?b+" "+a:a}}var i=this.options.get("containerCss")||{};return a.isFunction(i)&&(i=i(this.$element)),b.syncCssClasses(e,this.$element,g),e.css(i),e.addClass(f),e},d}),b.define("select2/compat/dropdownCss",["jquery","./utils"],function(a,b){function c(a){return null}function d(){}return d.prototype.render=function(d){var e=d.call(this),f=this.options.get("dropdownCssClass")||"";a.isFunction(f)&&(f=f(this.$element));var g=this.options.get("adaptDropdownCssClass");if(g=g||c,-1!==f.indexOf(":all:")){f=f.replace(":all:","");var h=g;g=function(a){var b=h(a);return null!=b?b+" "+a:a}}var i=this.options.get("dropdownCss")||{};return a.isFunction(i)&&(i=i(this.$element)),b.syncCssClasses(e,this.$element,g),e.css(i),e.addClass(f),e},d}),b.define("select2/compat/initSelection",["jquery"],function(a){function b(a,b,c){c.get("debug")&&window.console&&console.warn&&console.warn("Select2: The `initSelection` option has been deprecated in favor of a custom data adapter that overrides the `current` method. This method is now called multiple times instead of a single time when the instance is initialized. Support will be removed for the `initSelection` option in future versions of Select2"),this.initSelection=c.get("initSelection"),this._isInitialized=!1,a.call(this,b,c)}return b.prototype.current=function(b,c){var d=this;return this._isInitialized?void b.call(this,c):void this.initSelection.call(null,this.$element,function(b){d._isInitialized=!0,a.isArray(b)||(b=[b]),c(b)})},b}),b.define("select2/compat/inputData",["jquery"],function(a){function b(a,b,c){this._currentData=[],this._valueSeparator=c.get("valueSeparator")||",","hidden"===b.prop("type")&&c.get("debug")&&console&&console.warn&&console.warn("Select2: Using a hidden input with Select2 is no longer supported and may stop working in the future. It is recommended to use a ` + +
        +{% endblock %} + +{% block footer %} + + +{{ form.media }} + + +{% endblock %} diff --git a/test_project/select2_outside_admin/urls.py b/test_project/select2_outside_admin/urls.py new file mode 100644 index 000000000..f8aba24fc --- /dev/null +++ b/test_project/select2_outside_admin/urls.py @@ -0,0 +1,12 @@ +from django.urls import re_path as url + +from .views import UpdateView + + +urlpatterns = [ + url( + r'^$', + UpdateView.as_view(), + name='select2_outside_admin', + ), +] diff --git a/test_project/select2_outside_admin/views.py b/test_project/select2_outside_admin/views.py new file mode 100644 index 000000000..264b4864e --- /dev/null +++ b/test_project/select2_outside_admin/views.py @@ -0,0 +1,50 @@ +try: + from django.urls import reverse_lazy +except ImportError: + from django.core.urlresolvers import reverse_lazy +from django.forms import inlineformset_factory +from django.views import generic + +from select2_many_to_many.forms import TForm +from select2_many_to_many.models import TModel + + +class UpdateView(generic.UpdateView): + model = TModel + form_class = TForm + template_name = 'select2_outside_admin.html' + success_url = reverse_lazy('select2_outside_admin') + formset_class = inlineformset_factory( + TModel, + TModel, + form=TForm, + extra=1, + fk_name='for_inline', + fields=('name', 'test') + ) + + def get_object(self): + return TModel.objects.first() + + def post(self, request, *args, **kwargs): + self.object = self.get_object() + + form = self.get_form() + if form.is_valid() and self.formset.is_valid(): + return self.form_valid(form) + else: + return self.form_invalid(form) + + def form_valid(self, form): + result = super().form_valid(form) + self.formset.save() + return result + + @property + def formset(self): + if '_formset' not in self.__dict__: + setattr(self, '_formset', self.formset_class( + self.request.POST if self.request.method == 'POST' else None, + instance=getattr(self, 'object', self.get_object()), + )) + return self._formset diff --git a/test_project/select2_tagging/__init__.py b/test_project/select2_tagging/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/test_project/select2_tagging/admin.py b/test_project/select2_tagging/admin.py new file mode 100644 index 000000000..ea547b699 --- /dev/null +++ b/test_project/select2_tagging/admin.py @@ -0,0 +1,16 @@ +from django.contrib import admin + +from .forms import TForm +from .models import TModel + + +class TestInline(admin.TabularInline): + form = TForm + model = TModel + fk_name = 'for_inline' + + +class TestAdmin(admin.ModelAdmin): + form = TForm + inlines = [TestInline] +admin.site.register(TModel, TestAdmin) diff --git a/test_project/select2_tagging/forms.py b/test_project/select2_tagging/forms.py new file mode 100644 index 000000000..46c759d38 --- /dev/null +++ b/test_project/select2_tagging/forms.py @@ -0,0 +1,18 @@ +from dal import autocomplete + +from django import forms + +from tagging.forms import TagField + +from .models import TModel + + +class TForm(forms.ModelForm): + test = TagField( + widget=autocomplete.TaggingSelect2('select2_tagging'), + required=False, + ) + + class Meta: + model = TModel + exclude = ['for_inline'] diff --git a/test_project/select2_tagging/models.py b/test_project/select2_tagging/models.py new file mode 100644 index 000000000..2ae0674bf --- /dev/null +++ b/test_project/select2_tagging/models.py @@ -0,0 +1,23 @@ +from django.db import models + +from six import python_2_unicode_compatible + +from tagging.fields import TagField + + +@python_2_unicode_compatible +class TModel(models.Model): + name = models.CharField(max_length=200) + + test = TagField() + + for_inline = models.ForeignKey( + 'self', + models.CASCADE, + null=True, + blank=True, + related_name='inline_test_models' + ) + + def __str__(self): + return self.name diff --git a/test_project/select2_tagging/test_forms.py b/test_project/select2_tagging/test_forms.py new file mode 100644 index 000000000..6d79344b4 --- /dev/null +++ b/test_project/select2_tagging/test_forms.py @@ -0,0 +1,65 @@ +from django import forms +from django import http +from django import test +try: + from django.urls import reverse +except ImportError: + from django.core.urlresolvers import reverse + +import six + +from tagging.models import Tag + +from .forms import TForm +from .models import TModel + + +class TagSelect2TestMixin(object): + maxDiff = 3000 + + def test_save(self): + existing_tag = 'existing' + self.id()[:30] + self.tag.objects.create(name=existing_tag) + new_tag = 'new' + self.id()[:30] + + form = self.form(http.QueryDict('name=%s&test=%s&test=%s' % ( + self.id(), existing_tag, new_tag))) + + instance = form.save() + self.assertEqual( + instance.test, + six.text_type(',').join([existing_tag, new_tag]) + ) + + def test_initial(self): + tag = self.tag.objects.create(name='tag' + self.id()[:30]) + fixture = self.model.objects.create(name=self.id()[:30]) + fixture.test = tag.name + + # Instanciate the modelform for that instance + form = self.form(instance=fixture) + + # Ensure that the widget rendered right, with only the selection + self.assertHTMLEqual( + forms.SelectMultiple( + choices=( + (six.text_type(tag), six.text_type(tag)), + ), + attrs={ + 'data-autocomplete-light-function': 'select2', + 'data-autocomplete-light-url': reverse(self.url_name), + 'data-tags': 1, + 'id': 'id_test', + } + ).render('test', value=[ + six.text_type(tag), + ], attrs={'required': False}), + six.text_type(form['test'].as_widget()) + ) + + +class TaggitFormTest(TagSelect2TestMixin, test.TestCase): + form = TForm + model = TModel + tag = Tag + url_name = 'select2_tagging' diff --git a/test_project/select2_tagging/test_functional.py b/test_project/select2_tagging/test_functional.py new file mode 100644 index 000000000..c5ca1caa4 --- /dev/null +++ b/test_project/select2_tagging/test_functional.py @@ -0,0 +1,40 @@ +from dal.test import case, stories + +from dal_select2.test import Select2Story + +from taggit.models import Tag + +from .models import TModel + + +class TagSelect2AdminTestMixin(Select2Story, case.AdminMixin): + def setUp(self): + super(TagSelect2AdminTestMixin, self).setUp() + self.get(url=self.get_modeladmin_url('add')) + self.fill_name() + + def test_can_select_options(self): + labels = [self.id() + '0', self.id() + '1'] + self.tag_model.objects.create(name=labels[0]) + story = stories.SelectOptionMultiple(self) + + for option in labels: + story.select_option(option) + + # assert it works with new tags + story.assert_labels(labels) + story.assert_values(labels) + + # assert tags were saved, they have a pk + story.submit() + story.assert_labels(labels) + story.assert_values(labels) + + +class TaggitSelect2AdminTest(TagSelect2AdminTestMixin, + case.AutocompleteTestCase): + + field_name = 'test' + inline_related_name = 'inline_test_models' + model = TModel + tag_model = Tag diff --git a/test_project/select2_tagging/urls.py b/test_project/select2_tagging/urls.py new file mode 100644 index 000000000..ff542c65e --- /dev/null +++ b/test_project/select2_tagging/urls.py @@ -0,0 +1,16 @@ +from dal import autocomplete + +from django.urls import re_path as url + +from tagging.models import Tag + + +urlpatterns = [ + url( + 'test-autocomplete/$', + autocomplete.Select2QuerySetView.as_view( + queryset=Tag.objects.all(), + ), + name='select2_tagging', + ), +] diff --git a/test_project/select2_taggit/__init__.py b/test_project/select2_taggit/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/test_project/select2_taggit/admin.py b/test_project/select2_taggit/admin.py new file mode 100644 index 000000000..ea547b699 --- /dev/null +++ b/test_project/select2_taggit/admin.py @@ -0,0 +1,16 @@ +from django.contrib import admin + +from .forms import TForm +from .models import TModel + + +class TestInline(admin.TabularInline): + form = TForm + model = TModel + fk_name = 'for_inline' + + +class TestAdmin(admin.ModelAdmin): + form = TForm + inlines = [TestInline] +admin.site.register(TModel, TestAdmin) diff --git a/test_project/select2_taggit/forms.py b/test_project/select2_taggit/forms.py new file mode 100644 index 000000000..12b7ad33f --- /dev/null +++ b/test_project/select2_taggit/forms.py @@ -0,0 +1,14 @@ +from dal import autocomplete + +from django import forms + +from .models import TModel + + +class TForm(forms.ModelForm): + class Meta: + model = TModel + fields = ('name', 'test') + widgets = { + 'test': autocomplete.TaggitSelect2('select2_taggit') + } diff --git a/test_project/select2_taggit/migrations/0001_initial.py b/test_project/select2_taggit/migrations/0001_initial.py new file mode 100644 index 000000000..d2b9310f8 --- /dev/null +++ b/test_project/select2_taggit/migrations/0001_initial.py @@ -0,0 +1,26 @@ +# Generated by Django 2.0.6 on 2018-06-08 15:51 + +from django.db import migrations, models +import django.db.models.deletion +import taggit.managers + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('taggit', '0002_auto_20150616_2121'), + ] + + operations = [ + migrations.CreateModel( + name='TModel', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=200)), + ('for_inline', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='inline_test_models', to='select2_taggit.TModel')), + ('test', taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags')), + ], + ), + ] diff --git a/test_project/select2_taggit/migrations/__init__.py b/test_project/select2_taggit/migrations/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/test_project/select2_taggit/models.py b/test_project/select2_taggit/models.py new file mode 100644 index 000000000..e05b9c570 --- /dev/null +++ b/test_project/select2_taggit/models.py @@ -0,0 +1,23 @@ +from django.db import models + +from six import python_2_unicode_compatible + +from taggit.managers import TaggableManager + + +@python_2_unicode_compatible +class TModel(models.Model): + name = models.CharField(max_length=200) + + test = TaggableManager() + + for_inline = models.ForeignKey( + 'self', + models.CASCADE, + null=True, + blank=True, + related_name='inline_test_models' + ) + + def __str__(self): + return self.name diff --git a/test_project/select2_taggit/test_forms.py b/test_project/select2_taggit/test_forms.py new file mode 100644 index 000000000..ceba2724a --- /dev/null +++ b/test_project/select2_taggit/test_forms.py @@ -0,0 +1,85 @@ +import django +from django import forms +from django import http +from django import test +try: + from django.urls import reverse +except ImportError: + from django.core.urlresolvers import reverse + +import six + +from taggit.models import Tag + +from .forms import TForm +from .models import TModel + + +class TagSelect2TestMixin(object): + maxDiff = 3000 + + def test_save(self): + existing_tag = 'existing' + self.id() + existing = self.tag.objects.create(name=existing_tag) + new_tag = 'new' + self.id() + + form = self.form(http.QueryDict('name=%s&test=%s&test=%s' % ( + self.id(), existing_tag, new_tag))) + + instance = form.save() + result = instance.test.all() + + new = self.tag.objects.get(name=new_tag) + + self.assertEqual(list(result), [existing, new]) + + self.assertEqual( + list(self.model.objects.get(pk=instance.pk).test.all()), + [existing, new] + ) + + def test_multi_words_tag(self): + multi_words_tag = self.tag.objects.create(name='multi words') + form = self.form(http.QueryDict('name=%s&test=%s' % ( + self.id(), multi_words_tag))) + + instance = form.save() + + self.assertEqual( + list(self.model.objects.get(pk=instance.pk).test.all()), + [multi_words_tag, ] + ) + + def test_initial(self): + tag = self.tag.objects.create(name='tag' + self.id()) + fixture = self.model.objects.create(name=self.id()) + fixture.test.add(tag) + + # Instanciate the modelform for that instance + form = self.form(instance=fixture) + + # Ensure that the widget rendered right, with only the selection + self.assertHTMLEqual( + forms.SelectMultiple( + choices=( + (six.text_type(tag), six.text_type(tag)), + ), + attrs={ + 'data-autocomplete-light-function': 'select2', + 'data-autocomplete-light-url': reverse(self.url_name), + 'data-autocomplete-light-language': 'en', + 'data-tags': ',', + 'id': 'id_test', + } + ).render('test', value=[ + six.text_type(tag), + ], attrs={'required': django.VERSION >= (1, 10)}), + six.text_type(form['test'].as_widget()) + ) + + +class TaggitFormTest(TagSelect2TestMixin, test.TestCase): + form = TForm + model = TModel + tag = Tag + url_name = 'select2_taggit' diff --git a/test_project/select2_taggit/test_functional.py b/test_project/select2_taggit/test_functional.py new file mode 100644 index 000000000..64f4c7739 --- /dev/null +++ b/test_project/select2_taggit/test_functional.py @@ -0,0 +1,51 @@ +from dal.test import case, stories + +from dal_select2.test import Select2Story + +from taggit.models import Tag + +from .models import TModel + + +class TagSelect2AdminTestMixin(Select2Story, case.AdminMixin): + def setUp(self): + super(TagSelect2AdminTestMixin, self).setUp() + self.get(url=self.get_modeladmin_url('add')) + self.fill_name() + + self.labels = [self.id() + '0', self.id() + '1'] + self.tag_model.objects.create(name=self.labels[0]) + + def test_can_select_options(self): + story = stories.SelectOptionMultiple(self) + + for option in self.labels: + story.select_option(option) + + # assert it works with new tags + story.assert_labels(self.labels) + story.assert_values([self.labels[0], self.labels[1]]) + + # assert tags were saved, they have a pk + story.submit() + story.assert_labels(self.labels) + + # With tags, values are labels + story.assert_values(self.labels) + + def test_can_select_option_in_first_inline(self): + story = stories.InlineSelectOptionMultiple(self, inline_number=0) + + for option in self.labels: + story.select_option(option) + + story.assert_selection_persists(self.labels, self.labels) + + +class TaggitSelect2AdminTest(TagSelect2AdminTestMixin, + case.AutocompleteTestCase): + + field_name = 'test' + inline_related_name = 'inline_test_models' + model = TModel + tag_model = Tag diff --git a/test_project/select2_taggit/urls.py b/test_project/select2_taggit/urls.py new file mode 100644 index 000000000..94929fe24 --- /dev/null +++ b/test_project/select2_taggit/urls.py @@ -0,0 +1,16 @@ +from dal import autocomplete + +from django.urls import re_path as url + +from taggit.models import Tag + + +urlpatterns = [ + url( + 'test-autocomplete/$', + autocomplete.Select2QuerySetView.as_view( + queryset=Tag.objects.all(), + ), + name='select2_taggit', + ), +] diff --git a/test_project/settings/__init__.py b/test_project/settings/__init__.py new file mode 100644 index 000000000..9b5ed21c9 --- /dev/null +++ b/test_project/settings/__init__.py @@ -0,0 +1 @@ +from .base import * diff --git a/test_project/settings/base.py b/test_project/settings/base.py new file mode 100644 index 000000000..897d99155 --- /dev/null +++ b/test_project/settings/base.py @@ -0,0 +1,201 @@ +import os +import sys +import django + +# hack for pytest: +sys.path.insert(0, os.path.join( + os.path.dirname(__file__), '..', '..')) + +DEBUG = os.environ.get('DEBUG', False) + +if 'DEBUG' not in os.environ: + for cmd in ('runserver', 'pytest', 'py.test'): + if cmd in sys.argv[0] or len(sys.argv) > 1 and cmd in sys.argv[1]: + DEBUG = True + continue +TEMPLATE_DEBUG = DEBUG +LOG_LEVEL = os.environ.get('DJANGO_LOG_LEVEL', 'INFO') + +PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + +DATA_DIR = os.environ.get('OPENSHIFT_DATA_DIR', 'data') +if not os.path.exists(DATA_DIR): + os.makedirs(DATA_DIR) + +LOG_DIR = os.environ.get('OPENSHIFT_LOG_DIR', 'log') +if not os.path.exists(LOG_DIR): + os.makedirs(LOG_DIR) + +PUBLIC_DIR = os.path.join(os.environ.get('OPENSHIFT_REPO_DIR', ''), 'wsgi/static') + +if 'OPENSHIFT_POSTGRESQL_DB_HOST' in os.environ: + DATABASES['default']['NAME'] = os.environ['OPENSHIFT_APP_NAME'] + DATABASES['default']['USER'] = os.environ['OPENSHIFT_POSTGRESQL_DB_USERNAME'] + DATABASES['default']['PASSWORD'] = os.environ['OPENSHIFT_POSTGRESQL_DB_PASSWORD'] + DATABASES['default']['HOST'] = os.environ['OPENSHIFT_POSTGRESQL_DB_HOST'] + DATABASES['default']['PORT'] = os.environ['OPENSHIFT_POSTGRESQL_DB_PORT'] + DATABASES['default']['ENGINE'] = 'django.db.backends.postgresql_psycopg2' + +SESSION_SERIALIZER = 'django.contrib.sessions.serializers.JSONSerializer' + + +def get_databases(base_dir): + return { + 'default': { + 'ENGINE': os.environ.get('DJANGO_DB_ENGINE', + 'django.db.backends.sqlite3'), + 'NAME': os.environ.get('DJANGO_DB_NAME', + os.path.join(base_dir, 'db.sqlite3')), + 'USER': os.environ.get('DJANGO_DB_USER', ''), + } + } + + +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + +INSTALLED_APPS = [ + # Django apps + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + + # test apps + 'select2_foreign_key', + 'select2_list', + 'select2_generic_foreign_key', + 'select2_many_to_many', + 'select2_one_to_one', + 'select2_outside_admin', + 'select2_nestedadmin', + 'secure_data', + 'linked_data', + 'rename_forward', + 'forward_different_fields', + 'custom_select2', + 'select2_djhacker_formfield', + + # unit test app + 'tests', + + # Autocomplete + 'dal', + # Enable plugins + 'dal_select2', + 'queryset_sequence', + 'dal_queryset_sequence', + 'select2_taggit', + 'taggit', + 'nested_admin', + + # Project apps + 'django_extensions', +] + +if django.VERSION < (2, 0, 0): + # pending upstream support for dj 2.0 + INSTALLED_APPS += [ + 'gm2m', + 'select2_gm2m', + 'genericm2m', + 'select2_generic_m2m', + 'select2_tagging', + 'tagging', + ] + +INSTALLED_APPS = INSTALLED_APPS + ['django.contrib.admin'] + +DATABASES = get_databases(BASE_DIR) + +ROOT_URLCONF = 'urls' +WSGI_APPLICATION = 'wsgi.application' + +SECRET_KEY = '58$1jvc332=lyfk_m^jl6ody$7pbk18nm95==!r$7m5!2dp%l@' +ALLOWED_HOSTS = ['*'] + +MIDDLEWARE_CLASSES = [ + 'django.middleware.security.SecurityMiddleware', +] + +if not DEBUG: + MIDDLEWARE_CLASSES.append('whitenoise.middleware.WhiteNoiseMiddleware') + +MIDDLEWARE_CLASSES += [ + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +if DEBUG: + try: + import debug_toolbar + except ImportError: + pass + else: + INSTALLED_APPS.append('debug_toolbar') + MIDDLEWARE_CLASSES.append('debug_toolbar.middleware.DebugToolbarMiddleware') + MIDDLEWARE.append('debug_toolbar.middleware.DebugToolbarMiddleware') + +AUTH_PASSWORD_VALIDATORS = [] +DJANGO_LIVE_TEST_SERVER_ADDRESS = "localhost:8000-8010,8080,9200-9300" + +if not DEBUG: + STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage' + +LANGUAGE_CODE = 'en-us' +TIME_ZONE = 'UTC' +USE_I18N = True +if django.VERSION < (4, 0): + USE_L10N = True +USE_TZ = True + +SITE_ID = 1 + +STATIC_URL = '/public/static/' +STATIC_ROOT = os.path.join(PROJECT_ROOT, 'public', 'static') + +if DATA_DIR: + MEDIA_URL = '/static/media/' + MEDIA_ROOT = os.path.join(DATA_DIR, 'media') + +if PUBLIC_DIR: + STATIC_URL = '/static/collected/' + STATIC_ROOT = os.path.join(PUBLIC_DIR, 'collected') + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [ + os.path.join(os.path.join(PROJECT_ROOT, 'templates')), + ], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +SELENIUM_PAGE_LOAD_TIMEOUT = 100 +SELENIUM_TIMEOUT = 100 +INTERNAL_IPS = ('127.0.0.1',) diff --git a/test_project/templates/admin/login.html b/test_project/templates/admin/login.html new file mode 100644 index 000000000..7add01aa5 --- /dev/null +++ b/test_project/templates/admin/login.html @@ -0,0 +1,71 @@ +{% extends "admin/base_site.html" %} +{% load i18n static %} + +{% block extrastyle %}{{ block.super }} +{{ form.media }} +{% endblock %} + +{% block bodyclass %}{{ block.super }} login{% endblock %} + +{% block usertools %}{% endblock %} + +{% block nav-global %}{% endblock %} + +{% block content_title %}{% endblock %} + +{% block breadcrumbs %}{% endblock %} + +{% block content %} +{% if form.errors and not form.non_field_errors %} +

        +{% if form.errors.items|length == 1 %}{% trans "Please correct the error below." %}{% else %}{% trans "Please correct the errors below." %}{% endif %} +

        +{% endif %} + +{% if form.non_field_errors %} +{% for error in form.non_field_errors %} +

        + {{ error }} +

        +{% endfor %} +{% endif %} + +
        + +{% if user.is_authenticated %} +

        +{% blocktrans with username=request.user.username trimmed %} + You are authenticated as {{ username }}, but are not authorized to + access this page. Would you like to login to a different account? +{% endblocktrans %} +

        +{% endif %} + +
        {% csrf_token %} +
        + {{ form.username.errors }} + {{ form.username.label_tag }} {{ form.username }} +
        +
        + {{ form.password.errors }} + {{ form.password.label_tag }} {{ form.password }} + +
        + {% url 'admin_password_reset' as password_reset_url %} + {% if password_reset_url %} + + {% endif %} +
        + +
        +
        + +
        + + +{% endblock %} diff --git a/test_project/templates/base.html b/test_project/templates/base.html new file mode 100644 index 000000000..86a60e3e5 --- /dev/null +++ b/test_project/templates/base.html @@ -0,0 +1,69 @@ +{% extends "admin/base_site.html" %} +{% block branding %} +

        django-autocomplete-light demo: welcome

        +{% endblock %} + +{% block breadcrumbs %} + +{% endblock %} + +{% block content %} +
        +

        Welcome to django-autocomplete-light's demo project !

        +

        + Here are a number of example apps which you can test manually, or let + robots do the work and run ./manage.py test. +

        + +

        + And here are two very simple unadorned minimalist test pages for + isolated inspection and testing. +

        + +
        +
        +

        Need help ?

        +

        + If you can reproduce a bug in the demo project, it will be made a + priority to fix it, and add regression test to prevent it from occuring + again in the future. In this case, please clone the + Repository, configure the simplest example you can in a new app in + test_project and submit your pull request. +

        +

        + The easier you make it for us to reproduce your issue in an isolated + use-case, the best are your chances to have a satisfying answer. +

        +

        + Also, consider paid support + for django-autocomplete-light and Django in general ! + +

        +
        +{% endblock %} diff --git a/test_project/tests/__init__.py b/test_project/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/test_project/tests/admin.py b/test_project/tests/admin.py new file mode 100644 index 000000000..137fba549 --- /dev/null +++ b/test_project/tests/admin.py @@ -0,0 +1,4 @@ +from django.contrib import admin +from django.contrib.auth.models import User + +admin.site.unregister(User) diff --git a/test_project/tests/models.py b/test_project/tests/models.py new file mode 100644 index 000000000..325072e9d --- /dev/null +++ b/test_project/tests/models.py @@ -0,0 +1,15 @@ +from django.contrib.auth.models import User +from django.db.models.signals import post_migrate + + +def test_user(sender, *args, **kwargs): + if sender.name != 'django.contrib.auth': + return + + user, c = User.objects.get_or_create(username='test') + user.is_staff = True + user.is_superuser = True + user.set_password('test') + user.save() + +post_migrate.connect(test_user) diff --git a/test_project/tests/test_widgets.py b/test_project/tests/test_widgets.py new file mode 100644 index 000000000..bd906b927 --- /dev/null +++ b/test_project/tests/test_widgets.py @@ -0,0 +1,162 @@ +"""Test the base widget.""" + +from dal.autocomplete import Select2 +from dal.widgets import Select, WidgetMixin + +from dal_select2 import widgets as select2_widget + +import django +from django import forms +from django import http +from django import test +from django.urls import re_path as url +try: + from django.urls import reverse +except ImportError: + from django.core.urlresolvers import reverse +from django.test.utils import override_settings + +import mock + +import six + + +urlpatterns = [ + url( + r'^test-url/$', + mock.Mock(), + name='test_url' + ), +] + + +def selected_tag(): + return 'selected="selected"' if django.VERSION < (1, 11) else 'selected' + + +@override_settings(ROOT_URLCONF='tests.test_widgets') +class SelectTest(test.TestCase): # noqa + """Test case for the Select widget.""" + + def test_widget_renders_only_selected_with_url(self): + """Assert that it won't render unselected choices, if given a url.""" + class Form(forms.Form): + test = forms.ChoiceField( + choices=[(i, 'label for %s' % i) for i in range(0, 100)], + widget=Select(url=reverse('test_url')), + required=False + ) + + form = Form(http.QueryDict('test=4')) + expected = ''' + + '''.strip() % selected_tag() + + self.assertHTMLEqual(six.text_type(form['test'].as_widget()), expected) + + +@override_settings(ROOT_URLCONF='tests.test_widgets') +class Select2Test(test.TestCase): # noqa + """Test case for the Select2 widget.""" + + def test_widget_renders_empty_option_with_placeholder_without_url(self): + """Assert that it renders an empty option, if not given a url.""" + class Form(forms.Form): + test = forms.ChoiceField( + choices=[(1, "A")], + widget=Select2(attrs={ + "data-placeholder": "Some placeholder", + }), + required=False + ) + + form = Form(http.QueryDict()) + expected = ''' + + '''.strip() % selected_tag() + observed = six.text_type(form['test'].as_widget()) + + self.assertHTMLEqual(observed, expected) + + def test_widget_no_empty_option_without_placeholder_without_url(self): + """Assert that it renders an empty option, if not given a url.""" + class Form(forms.Form): + test = forms.ChoiceField( + choices=[(1, "A")], + widget=Select2(), + required=False + ) + + form = Form(http.QueryDict()) + expected = ''' + + '''.strip() + observed = six.text_type(form['test'].as_widget()) + + self.assertHTMLEqual(observed, expected) + + form = Form(http.QueryDict('test=1')) + expected = ''' + + '''.strip() % selected_tag() + observed = six.text_type(form['test'].as_widget()) + + self.assertHTMLEqual(observed, expected) + + def test_widget_finds_correct_language(self): + """Assert that a valid language code is found correctly.""" + self.assertEqual(select2_widget.get_i18n_name('en'), 'en') + self.assertEqual(select2_widget.get_i18n_name('en-US'), 'en') + self.assertEqual(select2_widget.get_i18n_name('fr-US'), 'fr') + self.assertEqual(select2_widget.get_i18n_name('fr-FR'), 'fr') + self.assertEqual(select2_widget.get_i18n_name('sr-Cyrl'), 'sr-Cyrl') + self.assertEqual(select2_widget.get_i18n_name('zh-TW'), 'zh-TW') + self.assertEqual(select2_widget.get_i18n_name('zh-Hant'), 'zh-TW') + + def test_widget_does_not_find_incorrect_language(self): + """Assert that an invalid language code is not found.""" + self.assertEqual(select2_widget.get_i18n_name('ab'), None) + self.assertEqual(select2_widget.get_i18n_name('cd'), None) + self.assertEqual(select2_widget.get_i18n_name('ab-US'), None) + + +@override_settings(ROOT_URLCONF='tests.test_widgets') +class WidgetMixinTest(test.TestCase): # noqa + def test_widget_renders_without_attrs(self): + """Assert that render will fallback to field name if no id.""" + class BaseWidget(object): + def render(self, name, value, attrs=None): + return '' + + class Widget(WidgetMixin, BaseWidget): + def render_forward_conf(self, id): + return six.text_type(id) + + widget = Widget(forward=['test']) + + # no attrs + observed = widget.render('myname', '') + self.assertEqual(observed, 'myname') + + # attrs without id + observed = widget.render('myname', '', attrs={}) + self.assertEqual(observed, 'myname') + + # attrs with id + observed = widget.render('myname', '', attrs={'id': 'myid'}) + self.assertEqual(observed, 'myid') diff --git a/test_project/urls.py b/test_project/urls.py new file mode 100644 index 000000000..d52e0753d --- /dev/null +++ b/test_project/urls.py @@ -0,0 +1,48 @@ +import django +from django.conf import settings +from django.contrib import admin +from django.urls import include, re_path as url + +import views + +urlpatterns = [ + url(r'^$', views.IndexView.as_view()), + + url(r'^admin/', admin.site.urls), + url(r'^login/', views.LoginView.as_view()), + + url(r'^dal_single/', views.BasicDALView, name='isolated_dal_single'), + url(r'^dal_multi/', views.BasicDALMultiView, name='isolated_dal_multi'), + + url(r'^secure_data/', include('secure_data.urls')), + url(r'^linked_data/', include('linked_data.urls')), + url(r'^rename_forward/', include('rename_forward.urls')), + url(r'^forward_different_fields/', + include('forward_different_fields.urls')), + url(r'^select2_nestedadmin/', include('select2_nestedadmin.urls')), + + url(r'^select2_foreign_key/', include('select2_foreign_key.urls')), + url(r'^select2_list/', include('select2_list.urls')), + url(r'^select2_generic_foreign_key/', + include('select2_generic_foreign_key.urls')), + url(r'^select2_many_to_many/', + include('select2_many_to_many.urls')), + url(r'^select2_one_to_one/', include('select2_one_to_one.urls')), + + url(r'^select2_outside_admin/', include('select2_outside_admin.urls')), + url(r'^select2_taggit/', include('select2_taggit.urls')), + url(r'^nested_admin/', include('nested_admin.urls')), + url(r'^select2_djhacker_formfield/', include('select2_djhacker_formfield.urls')), +] + +if django.VERSION < (2, 0, 0): + # pending upstream support + urlpatterns += [ + url(r'^select2_tagging/', include('select2_tagging.urls')), + url(r'^select2_gm2m/', include('select2_gm2m.urls')), + url(r'^select2_generic_m2m/', include('select2_generic_m2m.urls')), + ] + +if 'debug_toolbar' in settings.INSTALLED_APPS: + import debug_toolbar + urlpatterns += [url(r'^__debug__/', include(debug_toolbar.urls)), ] diff --git a/test_project/views.py b/test_project/views.py new file mode 100644 index 000000000..a83d4cea4 --- /dev/null +++ b/test_project/views.py @@ -0,0 +1,85 @@ +try: + from django.contrib.auth.views import LoginView +except ImportError: # old django, lol it + from django.views.generic import TemplateView as LoginView + +from django.forms.models import ModelChoiceIterator, ModelMultipleChoiceField +from django.http.response import HttpResponse +from django.urls import reverse_lazy +from django.views import generic + +from dal import autocomplete + +from select2_many_to_many.models import TModel + + +class LoginView(LoginView): + template_name = 'login.html' + + +class IndexView(generic.TemplateView): + template_name = 'base.html' + + +def BasicDALView(request): + """ + A very basic DAL widget alone on a page for minimalist testing. Not a + dressed demo of what DAL can do, rather an isolated functional widget page + against which comparisons can be made if someone has a DAL widget not + working, + + This is the basic DAL (single selected) widget. + """ + js = """ + + """ + + dal_media = autocomplete.Select2().media + + url = reverse_lazy('select2_many_to_many_autocomplete') + field = ModelMultipleChoiceField(TModel.objects.all()) + + widget = autocomplete.ModelSelect2( + url=url, + attrs={"class": "selector", "id": id, "data-placeholder": "Placeholder"}) + + widget.choices = ModelChoiceIterator(field) + + default = None + widget_html = widget.render(TModel.__name__, default) + + html = f"{js}\n{dal_media}

        {widget_html}

        " + + return HttpResponse(html) + + +def BasicDALMultiView(request): + """ + A very basic DAL widget alone on a page for minimalist testing. Not a dressed demo of what DAL can do, + rather an isolated functional widget page against which comparisons can be made if someone has a DAL + widget not working, + + This is the multi select DAL widget. + """ + js = """ + + """ + # + # + # + + dal_media = autocomplete.Select2().media + + url = reverse_lazy('select2_many_to_many_autocomplete') + field = ModelMultipleChoiceField(TModel.objects.all()) + + widget = autocomplete.ModelSelect2Multiple( + url=url, attrs={"class": "selector", "id": id, "data-placeholder": "Placeholder"}) + widget.choices = ModelChoiceIterator(field) + + default = None + widget_html = widget.render(TModel.__name__, default) + + html = f"{js}\n{dal_media}

        {widget_html}

        " + + return HttpResponse(html) diff --git a/test_project/wsgi.py b/test_project/wsgi.py new file mode 100644 index 000000000..315127f93 --- /dev/null +++ b/test_project/wsgi.py @@ -0,0 +1,22 @@ +""" +WSGI config for project project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/1.9/howto/deployment/wsgi/ +""" + +import os +import sys + +from django.core.wsgi import get_wsgi_application + +sys.path.insert( + 0, + os.path.abspath(os.path.join(os.path.dirname(__file__))) +) + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings") + +application = get_wsgi_application() diff --git a/tox.ini b/tox.ini new file mode 100644 index 000000000..3ae9ecf0f --- /dev/null +++ b/tox.ini @@ -0,0 +1,63 @@ +[tox] +envlist = + py{36,37,38,39,310,py3}-dj32 + py{38,39,310,py3}-dj40 + py{38,39,310,py3}-dj41 + py{38,39,310,py3}-dj42 + py{38,39,310,py3}-djmain + +[testenv] +changedir = {toxinidir}/test_project +usedevelop = true +setenv = + DEBUG=1 + DJANGO_SETTINGS_MODULE=settings.base + +commands = + pytest -v --cov --liveserver 127.0.0.1:9999 {posargs} secure_data rename_forward select2_foreign_key select2_generic_foreign_key select2_list select2_many_to_many select2_one_to_one select2_outside_admin select2_taggit custom_select2 select2_nestedadmin select2_djhacker_formfield + +deps = + dj32: Django==3.2.* + dj40: Django==4.0.* + dj41: Django==4.1.* + dj41: Django==4.2.* + djmain: https://github.com/django/django/archive/main.tar.gz + -rtest_project/requirements.txt + +passenv = + DISPLAY + XAUTHORITY + XDG_* + PIP_* + BROWSER + MOZ_HEADLESS + +[testenv:checkqa] +changedir = {toxinidir} +commands = + flake8 --show-source --max-complexity=8 --ignore=W503,D203,E722 --exclude src/dal/autocomplete.py,tests src + flake8 --show-source --max-complexity=7 --ignore=D203,F401 src/dal/autocomplete.py + flake8 --show-source --max-complexity=7 --ignore=D100,D101,D102,D103,D104,D105,D106 --filename=test_*.py src + flake8 --show-source --exclude migrations,settings --max-complexity=4 --ignore=D100,D101,D102,D103,D104,D105,D106,E305,W605 test_project + +deps = + flake8 + flake8-debugger + flake8-docstrings + flake8-import-order + mccabe + pep8-naming + pydocstyle<4 + +[testenv:docs] +changedir = {toxinidir}/docs +deps = + sphinx +commands = + pip install -r requirements.txt + pip install {toxinidir} + make html SPHINXOPTS="-W --keep-going -n" +whitelist_externals = make + +[flake8] +max-line-length = 88