From cebc979282c80ace1b2d4bb1388d32543f5f4961 Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Tue, 25 Nov 2014 16:47:48 +0000 Subject: [PATCH] Summary: Resolve person/email/document multiselect issue by importing select2 and switching the widgets over to using that. Port the milestones editing page to Bootstrap. - Legacy-Id: 8713 --- ietf/doc/fields.py | 47 +- ietf/doc/tests.py | 4 +- ietf/doc/tests_draft.py | 12 +- ietf/doc/urls.py | 2 +- ietf/doc/views_draft.py | 35 +- ietf/doc/views_search.py | 6 +- ietf/group/edit.py | 12 +- ietf/group/info.py | 2 +- ietf/group/milestones.py | 130 ++-- ietf/group/tests_info.py | 28 +- ietf/group/views_stream.py | 4 +- ietf/nomcom/forms.py | 4 +- ietf/person/fields.py | 60 +- ietf/person/tests.py | 2 +- ietf/person/urls.py | 2 +- ietf/person/views.py | 13 +- ietf/secr/drafts/forms.py | 4 +- ietf/secr/sreq/forms.py | 4 +- ietf/secr/templates/drafts/edit.html | 55 +- ietf/secr/templates/sreq/edit.html | 55 +- ietf/secr/templates/sreq/new.html | 55 +- ietf/templates/doc/change_shepherd.html | 9 +- ietf/templates/doc/draft/change_replaces.html | 17 +- ietf/templates/doc/draft/edit_info.html | 2 +- ietf/templates/group/edit.html | 31 +- ietf/templates/group/edit_milestones.html | 136 ++-- ietf/templates/group/milestone_form.html | 47 +- ietf/templates/group/stream_edit.html | 11 +- ietf/templates/nomcom/edit_position.html | 7 +- static/facelift/css/ietf.css | 14 + static/facelift/css/lib/select2-bootstrap.css | 497 +++++++++++++ static/facelift/css/lib/select2-spinner.gif | Bin 0 -> 1849 bytes static/facelift/css/lib/select2.css | 704 ++++++++++++++++++ static/facelift/css/lib/select2.png | Bin 0 -> 613 bytes static/facelift/js/edit-milestones.js | 140 ++++ static/facelift/js/ietf.js | 195 ++--- static/facelift/js/lib/select2-3.5.2.min.js | 23 + 37 files changed, 1853 insertions(+), 516 deletions(-) create mode 100644 static/facelift/css/lib/select2-bootstrap.css create mode 100644 static/facelift/css/lib/select2-spinner.gif create mode 100644 static/facelift/css/lib/select2.css create mode 100644 static/facelift/css/lib/select2.png create mode 100644 static/facelift/js/edit-milestones.js create mode 100644 static/facelift/js/lib/select2-3.5.2.min.js diff --git a/ietf/doc/fields.py b/ietf/doc/fields.py index 264beb08e..275f77dc4 100644 --- a/ietf/doc/fields.py +++ b/ietf/doc/fields.py @@ -8,17 +8,16 @@ import debug # pyflakes:ignore from ietf.doc.models import Document, DocAlias -def tokeninput_id_doc_name_json(objs): - return json.dumps([{ "id": o.pk, "name": escape(o.name) } for o in objs]) +def select2_id_doc_name_json(objs): + return json.dumps([{ "id": o.pk, "text": escape(o.name) } for o in objs]) -class AutocompletedDocumentsField(forms.CharField): - """Tokenizing autocompleted multi-select field for choosing - documents using jquery.tokeninput.js. +class SearchableDocumentsField(forms.CharField): + """Server-based multi-select field for choosing documents using + select2.js. The field uses a comma-separated list of primary keys in a - CharField element as its API, the tokeninput Javascript adds some - selection magic on top of this so we have to pass it a JSON - representation of ids and user-understandable labels.""" + CharField element as its API with some extra attributes used by + the Javascript part.""" def __init__(self, max_entries=None, # max number of selected objs @@ -31,39 +30,45 @@ class AutocompletedDocumentsField(forms.CharField): self.doc_type = doc_type self.model = model - super(AutocompletedDocumentsField, self).__init__(*args, **kwargs) + super(SearchableDocumentsField, self).__init__(*args, **kwargs) - self.widget.attrs["class"] = "tokenized-field" - self.widget.attrs["data-hint-text"] = hint_text + self.widget.attrs["class"] = "select2-field" + self.widget.attrs["data-placeholder"] = hint_text if self.max_entries != None: self.widget.attrs["data-max-entries"] = self.max_entries - def parse_tokenized_value(self, value): + def parse_select2_value(self, value): return [x.strip() for x in value.split(",") if x.strip()] def prepare_value(self, value): if not value: value = "" if isinstance(value, basestring): - pks = self.parse_tokenized_value(value) - value = self.model.objects.filter(pk__in=pks, type=self.doc_type) + pks = self.parse_select2_value(value) + value = self.model.objects.filter(pk__in=pks) + filter_args = {} + if self.model == DocAlias: + filter_args["document__type"] = self.doc_type + else: + filter_args["type"] = self.doc_type + value = value.filter(**filter_args) if isinstance(value, self.model): value = [value] - self.widget.attrs["data-pre"] = tokeninput_id_doc_name_json(value) + self.widget.attrs["data-pre"] = select2_id_doc_name_json(value) # doing this in the constructor is difficult because the URL # patterns may not have been fully constructed there yet - self.widget.attrs["data-ajax-url"] = urlreverse("ajax_tokeninput_search_docs", kwargs={ + self.widget.attrs["data-ajax-url"] = urlreverse("ajax_select2_search_docs", kwargs={ "doc_type": self.doc_type, "model_name": self.model.__name__.lower() }) - return ",".join(o.pk for o in value) + return u",".join(unicode(o.pk) for o in value) def clean(self, value): - value = super(AutocompletedDocumentsField, self).clean(value) - pks = self.parse_tokenized_value(value) + value = super(SearchableDocumentsField, self).clean(value) + pks = self.parse_select2_value(value) objs = self.model.objects.filter(pk__in=pks) @@ -77,7 +82,7 @@ class AutocompletedDocumentsField(forms.CharField): return objs -class AutocompletedDocAliasField(AutocompletedDocumentsField): +class SearchableDocAliasesField(SearchableDocumentsField): def __init__(self, model=DocAlias, *args, **kwargs): - super(AutocompletedDocAliasField, self).__init__(model=model, *args, **kwargs) + super(SearchableDocAliasesField, self).__init__(model=model, *args, **kwargs) diff --git a/ietf/doc/tests.py b/ietf/doc/tests.py index ab7169c0c..a8a9a16f5 100644 --- a/ietf/doc/tests.py +++ b/ietf/doc/tests.py @@ -127,7 +127,7 @@ class SearchTestCase(TestCase): draft = make_test_data() # Document - url = urlreverse("ajax_tokeninput_search_docs", kwargs={ + url = urlreverse("ajax_select2_search_docs", kwargs={ "model_name": "document", "doc_type": "draft", }) @@ -139,7 +139,7 @@ class SearchTestCase(TestCase): # DocAlias doc_alias = draft.docalias_set.get() - url = urlreverse("ajax_tokeninput_search_docs", kwargs={ + url = urlreverse("ajax_select2_search_docs", kwargs={ "model_name": "docalias", "doc_type": "draft", }) diff --git a/ietf/doc/tests_draft.py b/ietf/doc/tests_draft.py index eefe9b6ef..f76a81e55 100644 --- a/ietf/doc/tests_draft.py +++ b/ietf/doc/tests_draft.py @@ -1227,8 +1227,7 @@ class ChangeReplacesTests(TestCase): # Post that says replacea replaces base a self.assertEqual(self.basea.get_state().slug,'active') - repljson='{"%d":"%s"}'%(DocAlias.objects.get(name=self.basea.name).id,self.basea.name) - r = self.client.post(url, dict(replaces=repljson)) + r = self.client.post(url, dict(replaces=str(DocAlias.objects.get(name=self.basea.name).id))) self.assertEqual(r.status_code, 302) self.assertEqual(RelatedDocument.objects.filter(relationship__slug='replaces',source=self.replacea).count(),1) self.assertEqual(Document.objects.get(name='draft-test-base-a').get_state().slug,'repl') @@ -1236,23 +1235,20 @@ class ChangeReplacesTests(TestCase): # Post that says replaceboth replaces both base a and base b url = urlreverse('doc_change_replaces', kwargs=dict(name=self.replaceboth.name)) self.assertEqual(self.baseb.get_state().slug,'expired') - repljson='{"%d":"%s","%d":"%s"}'%(DocAlias.objects.get(name=self.basea.name).id,self.basea.name, - DocAlias.objects.get(name=self.baseb.name).id,self.baseb.name) - r = self.client.post(url, dict(replaces=repljson)) + r = self.client.post(url, dict(replaces=str(DocAlias.objects.get(name=self.basea.name).id) + "," + str(DocAlias.objects.get(name=self.baseb.name).id))) self.assertEqual(r.status_code, 302) self.assertEqual(Document.objects.get(name='draft-test-base-a').get_state().slug,'repl') self.assertEqual(Document.objects.get(name='draft-test-base-b').get_state().slug,'repl') # Post that undoes replaceboth - repljson='{}' - r = self.client.post(url, dict(replaces=repljson)) + r = self.client.post(url, dict(replaces="")) self.assertEqual(r.status_code, 302) self.assertEqual(Document.objects.get(name='draft-test-base-a').get_state().slug,'repl') # Because A is still also replaced by replacea self.assertEqual(Document.objects.get(name='draft-test-base-b').get_state().slug,'expired') # Post that undoes replacea url = urlreverse('doc_change_replaces', kwargs=dict(name=self.replacea.name)) - r = self.client.post(url, dict(replaces=repljson)) + r = self.client.post(url, dict(replaces="")) self.assertEqual(r.status_code, 302) self.assertEqual(Document.objects.get(name='draft-test-base-a').get_state().slug,'active') diff --git a/ietf/doc/urls.py b/ietf/doc/urls.py index e669d16b0..4a76ded78 100644 --- a/ietf/doc/urls.py +++ b/ietf/doc/urls.py @@ -49,7 +49,7 @@ urlpatterns = patterns('', url(r'^all/$', views_search.index_all_drafts, name="index_all_drafts"), url(r'^active/$', views_search.index_active_drafts, name="index_active_drafts"), - url(r'^tokeninputsearch/(?P(document|docalias))/(?Pdraft)/$', views_search.ajax_tokeninput_search_docs, name="ajax_tokeninput_search_docs"), + url(r'^select2search/(?P(document|docalias))/(?Pdraft)/$', views_search.ajax_select2_search_docs, name="ajax_select2_search_docs"), url(r'^(?P[A-Za-z0-9._+-]+)/(?:(?P[0-9-]+)/)?$', views_doc.document_main, name="doc_view"), url(r'^(?P[A-Za-z0-9._+-]+)/history/$', views_doc.document_history, name="doc_history"), diff --git a/ietf/doc/views_draft.py b/ietf/doc/views_draft.py index c2a08f708..70ec3ed06 100644 --- a/ietf/doc/views_draft.py +++ b/ietf/doc/views_draft.py @@ -1,6 +1,6 @@ # changing state and metadata on Internet Drafts -import datetime, json +import datetime from django import forms from django.http import HttpResponseRedirect, HttpResponseForbidden, Http404 @@ -24,13 +24,14 @@ from ietf.doc.utils import ( add_state_change_event, can_adopt_draft, get_tags_for_stream_id, nice_consensus, update_reminder, update_telechat, make_notify_changed_event, get_initial_notify ) from ietf.doc.lastcall import request_last_call +from ietf.doc.fields import SearchableDocAliasesField from ietf.group.models import Group, Role from ietf.iesg.models import TelechatDate from ietf.ietfauth.utils import has_role, is_authorized_in_doc_stream, user_is_person from ietf.ietfauth.utils import role_required from ietf.message.models import Message from ietf.name.models import IntendedStdLevelName, DocTagName, StreamName -from ietf.person.fields import AutocompletedEmailField +from ietf.person.fields import SearchableEmailField from ietf.person.models import Person, Email from ietf.secr.lib.template import jsonapi from ietf.utils.mail import send_mail, send_mail_message @@ -307,35 +308,21 @@ def collect_email_addresses(emails, doc): return emails class ReplacesForm(forms.Form): - replaces = forms.CharField(max_length=512,widget=forms.HiddenInput) + replaces = SearchableDocAliasesField(required=False) comment = forms.CharField(widget=forms.Textarea, required=False) def __init__(self, *args, **kwargs): self.doc = kwargs.pop('doc') super(ReplacesForm, self).__init__(*args, **kwargs) - drafts = {} - for d in self.doc.related_that_doc("replaces"): - drafts[d.id] = d.document.name - self.initial['replaces'] = json.dumps(drafts) + self.initial['replaces'] = self.doc.related_that_doc("replaces") def clean_replaces(self): - data = self.cleaned_data['replaces'].strip() - if data: - ids = [int(x) for x in json.loads(data)] - else: - return [] - objects = [] - for id in ids: - try: - d = DocAlias.objects.get(pk=id) - except DocAlias.DoesNotExist: - raise forms.ValidationError("ERROR: %s not found for id %d" % DocAlias._meta.verbos_name, id) + for d in self.cleaned_data['replaces']: if d.document == self.doc: - raise forms.ValidationError("ERROR: A draft can't replace itself") + raise forms.ValidationError("A draft can't replace itself") if d.document.type_id == "draft" and d.document.get_state_slug() == "rfc": - raise forms.ValidationError("ERROR: A draft can't replace an RFC") - objects.append(d) - return objects + raise forms.ValidationError("A draft can't replace an RFC") + return self.cleaned_data['replaces'] def replaces(request, name): """Change 'replaces' set of a Document of type 'draft' , notifying parties @@ -942,7 +929,7 @@ def edit_shepherd_writeup(request, name): context_instance=RequestContext(request)) class ShepherdForm(forms.Form): - shepherd = AutocompletedEmailField(required=False, only_users=True) + shepherd = SearchableEmailField(required=False, only_users=True) def edit_shepherd(request, name): """Change the shepherd for a Document""" @@ -968,7 +955,7 @@ def edit_shepherd(request, name): c.desc = "Document shepherd changed to "+ (doc.shepherd.person.name if doc.shepherd else "(None)") c.save() - if doc.shepherd.formatted_email() not in doc.notify: + if doc.shepherd and doc.shepherd.formatted_email() not in doc.notify: login = request.user.person addrs = doc.notify if addrs: diff --git a/ietf/doc/views_search.py b/ietf/doc/views_search.py index 1d0af9636..d75200d8c 100644 --- a/ietf/doc/views_search.py +++ b/ietf/doc/views_search.py @@ -45,7 +45,7 @@ from ietf.community.models import CommunityList from ietf.doc.models import ( Document, DocAlias, State, RelatedDocument, DocEvent, LastCallDocEvent, TelechatDocEvent, IESG_SUBSTATE_TAGS ) from ietf.doc.expire import expirable_draft -from ietf.doc.fields import tokeninput_id_doc_name_json +from ietf.doc.fields import select2_id_doc_name_json from ietf.group.models import Group from ietf.idindex.index import active_drafts_index_by_group from ietf.ipr.models import IprDocAlias @@ -629,7 +629,7 @@ def index_active_drafts(request): return render_to_response("doc/index_active_drafts.html", { 'groups': groups }, context_instance=RequestContext(request)) -def ajax_tokeninput_search_docs(request, model_name, doc_type): +def ajax_select2_search_docs(request, model_name, doc_type): if model_name == "docalias": model = DocAlias else: @@ -652,4 +652,4 @@ def ajax_tokeninput_search_docs(request, model_name, doc_type): objs = qs.distinct().order_by("name")[:20] - return HttpResponse(tokeninput_id_doc_name_json(objs), content_type='application/json') + return HttpResponse(select2_id_doc_name_json(objs), content_type='application/json') diff --git a/ietf/group/edit.py b/ietf/group/edit.py index c3b99a236..9b585c675 100644 --- a/ietf/group/edit.py +++ b/ietf/group/edit.py @@ -19,7 +19,7 @@ from ietf.group.models import ( Group, Role, GroupEvent, GroupHistory, GroupStat from ietf.group.utils import save_group_in_history, can_manage_group_type from ietf.group.utils import get_group_or_404 from ietf.ietfauth.utils import has_role -from ietf.person.fields import AutocompletedEmailsField +from ietf.person.fields import SearchableEmailsField from ietf.person.models import Person, Email from ietf.group.mails import email_iesg_secretary_re_charter @@ -29,11 +29,11 @@ class GroupForm(forms.Form): name = forms.CharField(max_length=255, label="Name", required=True) acronym = forms.CharField(max_length=10, label="Acronym", required=True) state = forms.ModelChoiceField(GroupStateName.objects.all(), label="State", required=True) - chairs = AutocompletedEmailsField(label="Chairs", required=False, only_users=True) - secretaries = AutocompletedEmailsField(label="Secretarias", required=False, only_users=True) - techadv = AutocompletedEmailsField(label="Technical Advisors", required=False, only_users=True) - delegates = AutocompletedEmailsField(label="Delegates", required=False, only_users=True, max_entries=MAX_GROUP_DELEGATES, - help_text=mark_safe("Chairs can delegate the authority to update the state of group documents - at most %s persons at a given time." % MAX_GROUP_DELEGATES)) + chairs = SearchableEmailsField(label="Chairs", required=False, only_users=True) + secretaries = SearchableEmailsField(label="Secretarias", required=False, only_users=True) + techadv = SearchableEmailsField(label="Technical Advisors", required=False, only_users=True) + delegates = SearchableEmailsField(label="Delegates", required=False, only_users=True, max_entries=MAX_GROUP_DELEGATES, + help_text=mark_safe("Chairs can delegate the authority to update the state of group documents - at most %s persons at a given time." % MAX_GROUP_DELEGATES)) ad = forms.ModelChoiceField(Person.objects.filter(role__name="ad", role__group__state="active").order_by('name'), label="Shepherding AD", empty_label="(None)", required=False) parent = forms.ModelChoiceField(Group.objects.filter(state="active").order_by('name'), empty_label="(None)", required=False) list_email = forms.CharField(max_length=64, required=False) diff --git a/ietf/group/info.py b/ietf/group/info.py index 54ccd7401..be445f7c5 100644 --- a/ietf/group/info.py +++ b/ietf/group/info.py @@ -303,7 +303,7 @@ def construct_group_menu_context(request, group, selected, group_type, others): if group.features.has_milestones: if group.state_id != "proposed" and (is_chair or can_manage): - actions.append((u"Add or edit milestones", urlreverse("group_edit_milestones", kwargs=kwargs))) + actions.append((u"Edit milestones", urlreverse("group_edit_milestones", kwargs=kwargs))) if group.features.has_materials and can_manage_materials(request.user, group): actions.append((u"Upload material", urlreverse("ietf.doc.views_material.choose_material_type", kwargs=kwargs))) diff --git a/ietf/group/milestones.py b/ietf/group/milestones.py index 0be402508..0815fc88b 100644 --- a/ietf/group/milestones.py +++ b/ietf/group/milestones.py @@ -2,99 +2,72 @@ import datetime import calendar -import json from django import forms -from django.http import HttpResponse, HttpResponseForbidden, HttpResponseBadRequest, HttpResponseRedirect, Http404 +from django.http import HttpResponseForbidden, HttpResponseBadRequest, HttpResponseRedirect, Http404 from django.shortcuts import render, redirect from django.contrib.auth.decorators import login_required -from ietf.doc.models import Document, DocEvent +from ietf.doc.models import DocEvent from ietf.doc.utils import get_chartering_type -from ietf.doc.fields import AutocompletedDocumentsField +from ietf.doc.fields import SearchableDocumentsField from ietf.group.models import GroupMilestone, MilestoneGroupEvent from ietf.group.utils import (save_milestone_in_history, can_manage_group_type, milestone_reviewer_for_group_type, get_group_or_404) from ietf.name.models import GroupMilestoneStateName from ietf.group.mails import email_milestones_changed - -def json_doc_names(docs): - return json.dumps([{"id": doc.pk, "name": doc.name } for doc in docs]) - -def parse_doc_names(s): - return Document.objects.filter(pk__in=[x.strip() for x in s.split(",") if x.strip()], type="draft") +from ietf.utils.fields import DatepickerDateField class MilestoneForm(forms.Form): id = forms.IntegerField(required=True, widget=forms.HiddenInput) - desc = forms.CharField(max_length=500, label="Milestone:", required=True) - due_month = forms.TypedChoiceField(choices=(), required=True, coerce=int) - due_year = forms.TypedChoiceField(choices=(), required=True, coerce=int) + desc = forms.CharField(max_length=500, label="Milestone", required=True) + due = DatepickerDateField(date_format="MM yyyy", picker_settings={"min-view-mode": "months", "autoclose": "1", "view-mode": "years" }, required=True) + docs = SearchableDocumentsField(label="Drafts", required=False, help_text="Any drafts that the milestone concerns.") resolved_checkbox = forms.BooleanField(required=False, label="Resolved") - resolved = forms.CharField(max_length=50, required=False) + resolved = forms.CharField(label="Resolved as", max_length=50, required=False) delete = forms.BooleanField(required=False, initial=False) - docs = AutocompletedDocumentsField(required=False) - - accept = forms.ChoiceField(choices=(("accept", "Accept"), ("reject", "Reject and delete"), ("noaction", "No action")), + review = forms.ChoiceField(label="Review action", help_text="Choose whether to accept or reject the proposed changes.", + choices=(("accept", "Accept"), ("reject", "Reject and delete"), ("noaction", "No action")), required=False, initial="noaction", widget=forms.RadioSelect) - def __init__(self, *args, **kwargs): - kwargs["label_suffix"] = "" - + def __init__(self, needs_review, reviewer, *args, **kwargs): m = self.milestone = kwargs.pop("instance", None) - self.needs_review = kwargs.pop("needs_review", False) - can_review = not self.needs_review + can_review = not needs_review if m: - self.needs_review = m.state_id == "review" + needs_review = m.state_id == "review" if not "initial" in kwargs: kwargs["initial"] = {} kwargs["initial"].update(dict(id=m.pk, desc=m.desc, - due_month=m.due.month, - due_year=m.due.year, + due=m.due, resolved_checkbox=bool(m.resolved), resolved=m.resolved, - docs=",".join(m.docs.values_list("pk", flat=True)), + docs=m.docs.all(), delete=False, - accept="noaction" if can_review and self.needs_review else None, + review="noaction" if can_review and needs_review else "", )) kwargs["prefix"] = "m%s" % m.pk super(MilestoneForm, self).__init__(*args, **kwargs) - # set choices for due date - this_year = datetime.date.today().year + self.fields["resolved"].widget.attrs["data-default"] = "Done" - self.fields["due_month"].choices = [(month, datetime.date(this_year, month, 1).strftime("%B")) for month in range(1, 13)] + if needs_review and self.milestone and self.milestone.state_id != "review": + self.fields["desc"].widget.attrs["readonly"] = True - years = [ y for y in range(this_year, this_year + 10)] + self.changed = False - initial = self.initial.get("due_year") - if initial and initial not in years: - years.insert(0, initial) + if not (needs_review and can_review): + self.fields["review"].widget = forms.HiddenInput() - self.fields["due_year"].choices = zip(years, map(str, years)) - - # figure out what to prepopulate many-to-many field with - pre = "" - if not self.is_bound: - pre = self.initial.get("docs", "") - else: - pre = self["docs"].data or "" - - # this is ugly, but putting it on self["docs"] is buggy with a - # bound/unbound form in Django 1.2 - self.docs_names = parse_doc_names(pre) - self.docs_prepopulate = json_doc_names(self.docs_names) - - # calculate whether we've changed - self.changed = self.is_bound and (not self.milestone or any(unicode(self[f].data) != unicode(self.initial[f]) for f in self.fields.iterkeys())) + self.needs_review = needs_review def clean_resolved(self): r = self.cleaned_data["resolved"].strip() @@ -134,14 +107,17 @@ def edit_milestones(request, acronym, group_type=None, milestone_set="current"): title = "Edit charter milestones for %s %s" % (group.acronym, group.type.name) milestones = group.groupmilestone_set.filter(state="charter") + reviewer = milestone_reviewer_for_group_type(group_type) + forms = [] milestones_dict = dict((str(m.id), m) for m in milestones) def due_month_year_to_date(c): - y = c["due_year"] - m = c["due_month"] - return datetime.date(y, m, calendar.monthrange(y, m)[1]) + y = c["due"].year + m = c["due"].month + first_day, last_day = calendar.monthrange(y, m) + return datetime.date(y, m, last_day) def set_attributes_from_form(f, m): c = f.cleaned_data @@ -153,10 +129,24 @@ def edit_milestones(request, acronym, group_type=None, milestone_set="current"): m.state = GroupMilestoneStateName.objects.get(slug="active") elif milestone_set == "charter": m.state = GroupMilestoneStateName.objects.get(slug="charter") + m.desc = c["desc"] m.due = due_month_year_to_date(c) m.resolved = c["resolved"] + def milestone_changed(f, m): + # we assume that validation has run + if not m or not f.is_valid(): + return True + + c = f.cleaned_data + return (c["desc"] != m.desc or + due_month_year_to_date(c) != m.due or + c["resolved"] != m.resolved or + set(c["docs"]) != set(m.docs.all()) or + c.get("review") in ("accept", "reject") + ) + def save_milestone_form(f): c = f.cleaned_data @@ -180,14 +170,14 @@ def edit_milestones(request, acronym, group_type=None, milestone_set="current"): changes = ['Changed %s' % named_milestone] - if m.state_id == "review" and not needs_review and c["accept"] != "noaction": + if m.state_id == "review" and not needs_review and c["review"] != "noaction": if not history: history = save_milestone_in_history(m) - if c["accept"] == "accept": + if c["review"] == "accept": m.state_id = "active" changes.append("set state to active from review, accepting new milestone") - elif c["accept"] == "reject": + elif c["review"] == "reject": m.state_id = "deleted" changes.append("set state to deleted from review, rejecting new milestone") @@ -257,8 +247,6 @@ def edit_milestones(request, acronym, group_type=None, milestone_set="current"): elif m.state_id == "review": return 'Added %s for review, due %s' % (named_milestone, m.due.strftime("%B %Y")) - finished_milestone_text = "Done" - form_errors = False if request.method == 'POST': @@ -269,22 +257,23 @@ def edit_milestones(request, acronym, group_type=None, milestone_set="current"): # new milestones have non-existing ids so instance end up as None instance = milestones_dict.get(request.POST.get(prefix + "-id", ""), None) - f = MilestoneForm(request.POST, prefix=prefix, instance=instance, - needs_review=needs_review) + f = MilestoneForm(needs_review, reviewer, request.POST, prefix=prefix, instance=instance) forms.append(f) form_errors = form_errors or not f.is_valid() + f.changed = milestone_changed(f, f.milestone) + if f.is_valid() and f.cleaned_data.get("review") in ("accept", "reject"): + f.needs_review = False + action = request.POST.get("action", "review") if action == "review": for f in forms: - if not f.is_valid(): - continue - - # let's fill in the form milestone so we can output it in the template - if not f.milestone: - f.milestone = GroupMilestone() - set_attributes_from_form(f, f.milestone) + if f.is_valid(): + # let's fill in the form milestone so we can output it in the template + if not f.milestone: + f.milestone = GroupMilestone() + set_attributes_from_form(f, f.milestone) elif action == "save" and not form_errors: changes = [] for f in forms: @@ -311,11 +300,11 @@ def edit_milestones(request, acronym, group_type=None, milestone_set="current"): return HttpResponseRedirect(group.about_url()) else: for m in milestones: - forms.append(MilestoneForm(instance=m, needs_review=needs_review)) + forms.append(MilestoneForm(needs_review, reviewer, instance=m)) can_reset = milestone_set == "charter" and get_chartering_type(group.charter) == "rechartering" - empty_form = MilestoneForm(needs_review=needs_review) + empty_form = MilestoneForm(needs_review, reviewer) forms.sort(key=lambda f: f.milestone.due if f.milestone else datetime.date.max) @@ -326,9 +315,8 @@ def edit_milestones(request, acronym, group_type=None, milestone_set="current"): form_errors=form_errors, empty_form=empty_form, milestone_set=milestone_set, - finished_milestone_text=finished_milestone_text, needs_review=needs_review, - reviewer=milestone_reviewer_for_group_type(group_type), + reviewer=reviewer, can_reset=can_reset)) @login_required diff --git a/ietf/group/tests_info.py b/ietf/group/tests_info.py index 83526dd39..e3dc8bf54 100644 --- a/ietf/group/tests_info.py +++ b/ietf/group/tests_info.py @@ -1,7 +1,6 @@ import os import shutil import calendar -import json import datetime from pyquery import PyQuery @@ -530,23 +529,21 @@ class MilestoneTests(TestCase): r = self.client.post(url, { 'prefix': "m-1", 'm-1-id': "-1", 'm-1-desc': "", # no description - 'm-1-due_month': str(due.month), - 'm-1-due_year': str(due.year), + 'm-1-due': due.strftime("%B %Y"), 'm-1-resolved': "", 'm-1-docs': ",".join(docs), 'action': "save", }) self.assertEqual(r.status_code, 200) q = PyQuery(r.content) - self.assertTrue(len(q('form ul.errorlist')) > 0) + self.assertTrue(len(q('form .has-error')) > 0) self.assertEqual(GroupMilestone.objects.count(), milestones_before) # add r = self.client.post(url, { 'prefix': "m-1", 'm-1-id': "-1", 'm-1-desc': "Test 3", - 'm-1-due_month': str(due.month), - 'm-1-due_year': str(due.year), + 'm-1-due': due.strftime("%B %Y"), 'm-1-resolved': "", 'm-1-docs': ",".join(docs), 'action': "save", @@ -580,8 +577,7 @@ class MilestoneTests(TestCase): r = self.client.post(url, { 'prefix': "m-1", 'm-1-id': -1, 'm-1-desc': "Test 3", - 'm-1-due_month': str(due.month), - 'm-1-due_year': str(due.year), + 'm-1-due': due.strftime("%B %Y"), 'm-1-resolved': "", 'm-1-docs': "", 'action': "save", @@ -612,11 +608,10 @@ class MilestoneTests(TestCase): r = self.client.post(url, { 'prefix': "m1", 'm1-id': m1.id, 'm1-desc': m1.desc, - 'm1-due_month': str(m1.due.month), - 'm1-due_year': str(m1.due.year), + 'm1-due': m1.due.strftime("%B %Y"), 'm1-resolved': m1.resolved, 'm1-docs': ",".join(m1.docs.values_list("name", flat=True)), - 'm1-accept': "accept", + 'm1-review': "accept", 'action': "save", }) self.assertEqual(r.status_code, 302) @@ -639,8 +634,7 @@ class MilestoneTests(TestCase): r = self.client.post(url, { 'prefix': "m1", 'm1-id': m1.id, 'm1-desc': m1.desc, - 'm1-due_month': str(m1.due.month), - 'm1-due_year': str(m1.due.year), + 'm1-due': m1.due.strftime("%B %Y"), 'm1-resolved': "", 'm1-docs': ",".join(m1.docs.values_list("name", flat=True)), 'm1-delete': "checked", @@ -670,15 +664,14 @@ class MilestoneTests(TestCase): r = self.client.post(url, { 'prefix': "m1", 'm1-id': m1.id, 'm1-desc': "", # no description - 'm1-due_month': str(due.month), - 'm1-due_year': str(due.year), + 'm1-due': due.strftime("%B %Y"), 'm1-resolved': "", 'm1-docs': ",".join(docs), 'action': "save", }) self.assertEqual(r.status_code, 200) q = PyQuery(r.content) - self.assertTrue(len(q('form ul.errorlist')) > 0) + self.assertTrue(len(q('form .has-error')) > 0) m = GroupMilestone.objects.get(pk=m1.pk) self.assertEqual(GroupMilestone.objects.count(), milestones_before) self.assertEqual(m.due, m1.due) @@ -688,8 +681,7 @@ class MilestoneTests(TestCase): r = self.client.post(url, { 'prefix': "m1", 'm1-id': m1.id, 'm1-desc': "Test 2 - changed", - 'm1-due_month': str(due.month), - 'm1-due_year': str(due.year), + 'm1-due': due.strftime("%B %Y"), 'm1-resolved': "Done", 'm1-resolved_checkbox': "checked", 'm1-docs': ",".join(docs), diff --git a/ietf/group/views_stream.py b/ietf/group/views_stream.py index 6fe223703..6ec34702c 100644 --- a/ietf/group/views_stream.py +++ b/ietf/group/views_stream.py @@ -10,7 +10,7 @@ from ietf.group.models import Group, GroupEvent, Role from ietf.group.utils import save_group_in_history from ietf.ietfauth.utils import has_role from ietf.name.models import StreamName -from ietf.person.fields import AutocompletedEmailsField +from ietf.person.fields import SearchableEmailsField from ietf.person.models import Email import debug # pyflakes:ignore @@ -33,7 +33,7 @@ def stream_documents(request, acronym): return render_to_response('group/stream_documents.html', {'stream':stream, 'docs':docs, 'meta':meta, 'editable':editable }, context_instance=RequestContext(request)) class StreamEditForm(forms.Form): - delegates = AutocompletedEmailsField(required=False, only_users=True) + delegates = SearchableEmailsField(required=False, only_users=True) def stream_edit(request, acronym): group = get_object_or_404(Group, acronym=acronym) diff --git a/ietf/nomcom/forms.py b/ietf/nomcom/forms.py index 7fba3b598..0aee2d491 100644 --- a/ietf/nomcom/forms.py +++ b/ietf/nomcom/forms.py @@ -17,7 +17,7 @@ from ietf.nomcom.utils import (NOMINATION_RECEIPT_TEMPLATE, FEEDBACK_RECEIPT_TEM get_user_email, validate_private_key, validate_public_key, get_or_create_nominee, create_feedback_email) from ietf.person.models import Email -from ietf.person.fields import AutocompletedEmailField +from ietf.person.fields import SearchableEmailField from ietf.utils.fields import MultiEmailField from ietf.utils.mail import send_mail @@ -657,7 +657,7 @@ class PositionForm(BaseNomcomForm, forms.ModelForm): fieldsets = [('Position', ('name', 'description', 'is_open', 'incumbent'))] - incumbent = AutocompletedEmailField(required=False) + incumbent = SearchableEmailField(required=False) class Meta: model = Position diff --git a/ietf/person/fields.py b/ietf/person/fields.py index 25ed5b889..031bb80cf 100644 --- a/ietf/person/fields.py +++ b/ietf/person/fields.py @@ -8,7 +8,7 @@ import debug # pyflakes:ignore from ietf.person.models import Email, Person -def tokeninput_id_name_json(objs): +def select2_id_name_json(objs): def format_email(e): return escape(u"%s <%s>" % (e.person.name, e.address)) def format_person(p): @@ -16,19 +16,19 @@ def tokeninput_id_name_json(objs): formatter = format_email if objs and isinstance(objs[0], Email) else format_person - return json.dumps([{ "id": o.pk, "name": formatter(o) } for o in objs]) + return json.dumps([{ "id": o.pk, "text": formatter(o) } for o in objs]) -class AutocompletedPersonsField(forms.CharField): - """Tokenizing autocompleted multi-select field for choosing - persons/emails or just persons using jquery.tokeninput.js. +class SearchablePersonsField(forms.CharField): + """Server-based multi-select field for choosing + persons/emails or just persons using select2.js. The field operates on either Email or Person models. In the case - of Email models, the person name is shown next to the email address. + of Email models, the person name is shown next to the email + address. The field uses a comma-separated list of primary keys in a - CharField element as its API, the tokeninput Javascript adds some - selection magic on top of this so we have to pass it a JSON - representation of ids and user-understandable labels.""" + CharField element as its API with some extra attributes used by + the Javascript part.""" def __init__(self, max_entries=None, # max number of selected objs @@ -41,38 +41,38 @@ class AutocompletedPersonsField(forms.CharField): self.only_users = only_users self.model = model - super(AutocompletedPersonsField, self).__init__(*args, **kwargs) + super(SearchablePersonsField, self).__init__(*args, **kwargs) - self.widget.attrs["class"] = "tokenized-field" - self.widget.attrs["data-hint-text"] = hint_text + self.widget.attrs["class"] = "select2-field" + self.widget.attrs["data-placeholder"] = hint_text if self.max_entries != None: self.widget.attrs["data-max-entries"] = self.max_entries - def parse_tokenized_value(self, value): + def parse_select2_value(self, value): return [x.strip() for x in value.split(",") if x.strip()] def prepare_value(self, value): if not value: value = "" if isinstance(value, basestring): - pks = self.parse_tokenized_value(value) + pks = self.parse_select2_value(value) value = self.model.objects.filter(pk__in=pks).select_related("person") if isinstance(value, self.model): value = [value] - self.widget.attrs["data-pre"] = tokeninput_id_name_json(value) + self.widget.attrs["data-pre"] = select2_id_name_json(value) # doing this in the constructor is difficult because the URL # patterns may not have been fully constructed there yet - self.widget.attrs["data-ajax-url"] = urlreverse("ajax_tokeninput_search", kwargs={ "model_name": self.model.__name__.lower() }) + self.widget.attrs["data-ajax-url"] = urlreverse("ajax_select2_search_person_email", kwargs={ "model_name": self.model.__name__.lower() }) if self.only_users: self.widget.attrs["data-ajax-url"] += "?user=1" # require a Datatracker account - return ",".join(e.address for e in value) + return u",".join(e.address for e in value) def clean(self, value): - value = super(AutocompletedPersonsField, self).clean(value) - pks = self.parse_tokenized_value(value) + value = super(SearchablePersonsField, self).clean(value) + pks = self.parse_select2_value(value) objs = self.model.objects.filter(pk__in=pks) if self.model == Email: @@ -92,32 +92,32 @@ class AutocompletedPersonsField(forms.CharField): return objs -class AutocompletedPersonField(AutocompletedPersonsField): - """Version of AutocompletedPersonsField specialized to a single object.""" +class SearchablePersonField(SearchablePersonsField): + """Version of SearchablePersonsField specialized to a single object.""" def __init__(self, *args, **kwargs): kwargs["max_entries"] = 1 - super(AutocompletedPersonField, self).__init__(*args, **kwargs) + super(SearchablePersonField, self).__init__(*args, **kwargs) def clean(self, value): - return super(AutocompletedPersonField, self).clean(value).first() + return super(SearchablePersonField, self).clean(value).first() -class AutocompletedEmailsField(AutocompletedPersonsField): - """Version of AutocompletedPersonsField with the defaults right for Emails.""" +class SearchableEmailsField(SearchablePersonsField): + """Version of SearchablePersonsField with the defaults right for Emails.""" def __init__(self, model=Email, hint_text="Type in name or email to search for person and email address.", *args, **kwargs): - super(AutocompletedEmailsField, self).__init__(model=model, hint_text=hint_text, *args, **kwargs) + super(SearchableEmailsField, self).__init__(model=model, hint_text=hint_text, *args, **kwargs) -class AutocompletedEmailField(AutocompletedEmailsField): - """Version of AutocompletedEmailsField specialized to a single object.""" +class SearchableEmailField(SearchableEmailsField): + """Version of SearchableEmailsField specialized to a single object.""" def __init__(self, *args, **kwargs): kwargs["max_entries"] = 1 - super(AutocompletedEmailField, self).__init__(*args, **kwargs) + super(SearchableEmailField, self).__init__(*args, **kwargs) def clean(self, value): - return super(AutocompletedEmailField, self).clean(value).first() + return super(SearchableEmailField, self).clean(value).first() diff --git a/ietf/person/tests.py b/ietf/person/tests.py index d044d45f5..80c239d98 100644 --- a/ietf/person/tests.py +++ b/ietf/person/tests.py @@ -11,7 +11,7 @@ class PersonTests(TestCase): draft = make_test_data() person = draft.ad - r = self.client.get(urlreverse("ietf.person.views.ajax_tokeninput_search", kwargs={ "model_name": "email"}), dict(q=person.name)) + r = self.client.get(urlreverse("ietf.person.views.ajax_select2_search", kwargs={ "model_name": "email"}), dict(q=person.name)) self.assertEqual(r.status_code, 200) data = json.loads(r.content) self.assertEqual(data[0]["id"], person.email_address()) diff --git a/ietf/person/urls.py b/ietf/person/urls.py index 8392fb51d..ce76d9f69 100644 --- a/ietf/person/urls.py +++ b/ietf/person/urls.py @@ -2,6 +2,6 @@ from django.conf.urls import patterns from ietf.person import ajax urlpatterns = patterns('', - (r'^search/(?P(person|email))/$', "ietf.person.views.ajax_tokeninput_search", None, 'ajax_tokeninput_search'), + (r'^search/(?P(person|email))/$', "ietf.person.views.ajax_select2_search", None, 'ajax_select2_search_person_email'), (r'^(?P[a-z0-9]+).json$', ajax.person_json), ) diff --git a/ietf/person/views.py b/ietf/person/views.py index 7e61e727a..8d3627c75 100644 --- a/ietf/person/views.py +++ b/ietf/person/views.py @@ -2,9 +2,9 @@ from django.http import HttpResponse from django.db.models import Q from ietf.person.models import Email, Person -from ietf.person.fields import tokeninput_id_name_json +from ietf.person.fields import select2_id_name_json -def ajax_tokeninput_search(request, model_name): +def ajax_select2_search(request, model_name): if model_name == "email": model = Email else: @@ -39,6 +39,11 @@ def ajax_tokeninput_search(request, model_name): if only_users: objs = objs.exclude(user=None) - objs = objs.distinct()[:10] + try: + page = int(request.GET.get("p", 1)) - 1 + except ValueError: + page = 0 - return HttpResponse(tokeninput_id_name_json(objs), content_type='application/json') + objs = objs.distinct()[page:page + 10] + + return HttpResponse(select2_id_name_json(objs), content_type='application/json') diff --git a/ietf/secr/drafts/forms.py b/ietf/secr/drafts/forms.py index 58d5a6ed6..c841668e3 100644 --- a/ietf/secr/drafts/forms.py +++ b/ietf/secr/drafts/forms.py @@ -8,7 +8,7 @@ from ietf.doc.models import Document, DocAlias, State from ietf.name.models import IntendedStdLevelName, DocRelationshipName from ietf.group.models import Group from ietf.person.models import Person, Email -from ietf.person.fields import AutocompletedEmailField +from ietf.person.fields import SearchableEmailField from ietf.secr.groups.forms import get_person @@ -132,7 +132,7 @@ class EditModelForm(forms.ModelForm): iesg_state = forms.ModelChoiceField(queryset=State.objects.filter(type='draft-iesg'),required=False) group = GroupModelChoiceField(required=True) review_by_rfc_editor = forms.BooleanField(required=False) - shepherd = AutocompletedEmailField(required=False, only_users=True) + shepherd = SearchableEmailField(required=False, only_users=True) class Meta: model = Document diff --git a/ietf/secr/sreq/forms.py b/ietf/secr/sreq/forms.py index 20cf544c3..02c6b9859 100644 --- a/ietf/secr/sreq/forms.py +++ b/ietf/secr/sreq/forms.py @@ -2,7 +2,7 @@ from django import forms from ietf.group.models import Group from ietf.meeting.models import ResourceAssociation -from ietf.person.fields import AutocompletedPersonsField +from ietf.person.fields import SearchablePersonsField # ------------------------------------------------- @@ -67,7 +67,7 @@ class SessionForm(forms.Form): wg_selector3 = forms.ChoiceField(choices=WG_CHOICES,required=False) third_session = forms.BooleanField(required=False) resources = forms.MultipleChoiceField(choices=[(x.pk,x.desc) for x in ResourceAssociation.objects.all()], widget=forms.CheckboxSelectMultiple,required=False) - bethere = AutocompletedPersonsField(label="Must be present", required=False) + bethere = SearchablePersonsField(label="Must be present", required=False) def __init__(self, *args, **kwargs): super(SessionForm, self).__init__(*args, **kwargs) diff --git a/ietf/secr/templates/drafts/edit.html b/ietf/secr/templates/drafts/edit.html index 1c29810f0..31d29d6e1 100644 --- a/ietf/secr/templates/drafts/edit.html +++ b/ietf/secr/templates/drafts/edit.html @@ -7,9 +7,58 @@ - - - + + + + + {% endblock %} {% block breadcrumbs %}{{ block.super }} diff --git a/ietf/secr/templates/sreq/edit.html b/ietf/secr/templates/sreq/edit.html index 1c80339fd..372e4f368 100755 --- a/ietf/secr/templates/sreq/edit.html +++ b/ietf/secr/templates/sreq/edit.html @@ -5,9 +5,58 @@ {% block extrahead %}{{ block.super }} - - - + + + + + {% endblock %} {% block breadcrumbs %}{{ block.super }} diff --git a/ietf/secr/templates/sreq/new.html b/ietf/secr/templates/sreq/new.html index c7af96eb0..450fa8f27 100755 --- a/ietf/secr/templates/sreq/new.html +++ b/ietf/secr/templates/sreq/new.html @@ -5,9 +5,58 @@ {% block extrahead %}{{ block.super }} - - - + + + + + {% endblock %} {% block breadcrumbs %}{{ block.super }} diff --git a/ietf/templates/doc/change_shepherd.html b/ietf/templates/doc/change_shepherd.html index 1f8b856b6..67b81b10b 100644 --- a/ietf/templates/doc/change_shepherd.html +++ b/ietf/templates/doc/change_shepherd.html @@ -7,8 +7,8 @@ Change document shepherd for {{ doc.name }}-{{ doc.rev }} {% endblock %} {% block pagehead %} - - + + {% endblock %} {% block content %} @@ -19,7 +19,7 @@ Change document shepherd for {{ doc.name }}-{{ doc.rev }} {% bootstrap_messages %} -
+ {% csrf_token %} {% bootstrap_form form %} @@ -31,6 +31,5 @@ Change document shepherd for {{ doc.name }}-{{ doc.rev }} {% endblock %} {% block js %} - - + {% endblock %} diff --git a/ietf/templates/doc/draft/change_replaces.html b/ietf/templates/doc/draft/change_replaces.html index 3b5253764..d09426f4e 100644 --- a/ietf/templates/doc/draft/change_replaces.html +++ b/ietf/templates/doc/draft/change_replaces.html @@ -7,23 +7,17 @@ {% bootstrap_messages %} {% block pagehead %} - - + + {% endblock %} {% block content %}

Change documents replaced by
{{ doc }}

- + {% csrf_token %} -
- - - Enter draft names, separated with commas. -
- - {% bootstrap_form form %} + {% bootstrap_form form %} {% buttons %} @@ -34,6 +28,5 @@ {% endblock %} {% block js %} - - + {% endblock %} diff --git a/ietf/templates/doc/draft/edit_info.html b/ietf/templates/doc/draft/edit_info.html index 0d3d52513..f29dee6b5 100644 --- a/ietf/templates/doc/draft/edit_info.html +++ b/ietf/templates/doc/draft/edit_info.html @@ -10,7 +10,7 @@ {% bootstrap_messages %} - + {% csrf_token %} {% bootstrap_form form %} diff --git a/ietf/templates/group/edit.html b/ietf/templates/group/edit.html index c4c8ac1af..fb3a0b204 100644 --- a/ietf/templates/group/edit.html +++ b/ietf/templates/group/edit.html @@ -12,20 +12,18 @@ {% endblock %} {% block pagehead %} - - + + {% endblock %} {% block content %}

{% if action == "edit" %} Edit {{ group.type.name }} {{ group.acronym }} +{% elif action == "charter" %} + Start chartering new group {% else %} - {% if action == "charter" %} - Start chartering new group - {% else %} - Create new group or BOF - {% endif %} + Create new group or BOF {% endif %}

@@ -35,7 +33,7 @@ chairs and delegates, need a datatracker account to actually do so. New accounts can be created here.

- + {% csrf_token %} {% bootstrap_form form layout='horizontal' %} @@ -43,18 +41,15 @@ so. New accounts can be created here.Submit Back - {% else %} - {% if action == "charter" %} - - {% else %} - - {% endif %} - {% endif %} - {% endbuttons %} + {% elif action == "charter" %} + + {% else %} + + {% endif %} + {% endbuttons %}
{% endblock %} {% block js %} - - + {% endblock %} diff --git a/ietf/templates/group/edit_milestones.html b/ietf/templates/group/edit_milestones.html index 80397bdaf..769f895ab 100644 --- a/ietf/templates/group/edit_milestones.html +++ b/ietf/templates/group/edit_milestones.html @@ -1,111 +1,99 @@ -{% extends "base.html" %} +{% extends "ietf.html" %} + +{% load bootstrap3 %} + +{% block pagehead %} + + + +{% endblock %} {% block title %}{{ title }}{% endblock %} -{% block morecss %} -tr.milestone td { padding: 0.2em 0; cursor: pointer; vertical-align: top; } -tr.milestone:hover { background-color: #e8f0fa; } -td.due { width: 5em; } -.milestone.changed { font-weight: bold; } -.milestone .note { font-style: italic; display: inline-block; margin-left: 0.5em; color: #2647a0; } -.milestone .doc { display: block; padding-left: 1em; } -.edit-milestone { display: none; } -.milestone.delete, .edit-milestone.delete, .edit-milestone.delete input { color: #aaa !important; } -.edit-milestone table { margin: 1em 0; } -.edit-milestone table td { padding: 0.1em; } -.edit-milestone .desc input { width: 50em; } -.edit-milestone .due input { width: 6em; } -.edit-milestone input[type=checkbox] { vertical-align: middle; margin: 0 0.2em 0 0.8em;} -.edit-milestone .resolved label { vertical-align: middle; } -.edit-milestone .delete label { vertical-align: middle; } -.edit-milestone .accept ul { display: inline-block; margin: 0; padding: 0; } -.edit-milestone .accept ul li { list-style: none; display: inline-block; margin: 0; padding: 0; padding-left: 0.4em; } -.edit-milestone .accept ul li label { vertical-align: middle; } -.edit-milestone .accept ul li input { margin: 0; padding: 0; vertical-align: middle; } -.edit-milestone .docs td { vertical-align: top; } - -ul.errorlist { border-width: 0px; padding: 0px; margin: 0px; display: inline-block; } -ul.errorlist li { color: #a00; margin: 0px; padding: 0px; list-style: none; } -p.help { font-style: italic; } -p.error { color: #a00; font-size: larger; } -tr.milestone.add { font-style: italic; } -{% endblock %} - -{% block pagehead %} - -{% endblock %} +{% bootstrap_messages %} {% block content %} -{% load ietf_filters %}

{{ title }}

Links: - {{ group.acronym }} {{ group.type.name }} + {{ group.acronym }} {{ group.type.name }} {% if group.charter %} - {{ group.charter.canonical_name }} {% endif %}

-

{% if forms %}Click a milestone to edit it.{% endif %} +

+ {% if forms %}Click a milestone to edit it.{% endif %} -{% if needs_review %} -Note that as {{ group.type.name }} Chair you cannot edit descriptions of existing -milestones and milestones you add are subject to review by the {{ reviewer }}. -{% endif %} + {% if needs_review %} + Note that as {{ group.type.name }} Chair you cannot edit descriptions of existing + milestones and milestones you add are subject to review by the {{ reviewer }}. + {% endif %}

{% if can_reset %}

-You can reset -this list to the milestones currently in use for the {{ group.acronym }} {{ group.type.name }}. + You can reset + this list to the milestones currently in use for the {{ group.acronym }} {{ group.type.name }}.

{% endif %} {% if form_errors %} -

There were errors, see below.

+

There were errors, see below.

{% endif %} -
{% csrf_token %} - -{% for form in forms %} - - - + + + -
- Cancel - +
+
{% if form.milestone.resolved %}{{ form.milestone.resolved }}{% else %}{{ form.milestone.due|date:"M Y" }}{% endif %} -
{{ form.milestone.desc }} - {% if form.needs_review %}awaiting accept{% endif %} - {% if form.changed %}changed{% endif %} -
+{% csrf_token %} + - {% for d in form.docs_names %} -
{{ d }}
+ {% for form in forms %} + + + + + + + + {% endfor %} - - - -{% endfor %} - - -
+ {% if form.milestone.resolved %} + {{ form.milestone.resolved }} + {% else %} + {{ form.milestone.due|date:"M Y" }} + {% endif %} + +
{{ form.milestone.desc }} + {% if form.needs_review %}Awaiting accept{% endif %} + {% if form.changed %}Changed{% endif %} +
+ + {% for d in form.docs_names %} +
{{ d }}
+ {% endfor %} +
{% include "group/milestone_form.html" %}
{% include "group/milestone_form.html" %}
Add {% if milestone_set == "chartering" %}charter{% endif%} milestone {% if needs_review %}for {{ reviewer }} review{% endif %}
{% include "group/milestone_form.html" with form=empty_form %}
+
{% include "group/milestone_form.html" with form=empty_form %}
+ + {% buttons %} + Cancel + + - + {% endbuttons %}
{% endblock %} -{% block content_end %} - - - - - +{% block js %} + + + {% endblock %} diff --git a/ietf/templates/group/milestone_form.html b/ietf/templates/group/milestone_form.html index 0a1a271fc..666c6d6a0 100644 --- a/ietf/templates/group/milestone_form.html +++ b/ietf/templates/group/milestone_form.html @@ -1,42 +1,9 @@ {# assumes group, form, needs_review, reviewer are in the context #} - -{{ form.id }} - - - - - - {% if form.desc.errors %}{% endif %} - - - - - - - - - {% if form.needs_review %} - - - - - {% endif %} -
{{ form.desc.label_tag }} - - {% if needs_review and form.milestone and form.milestone.state_id != "review" %} - {{ form.milestone.desc }} {{ form.desc.as_hidden }} - {% else %} - {{ form.desc }} - {% endif %} - - {{ form.delete }} {{ form.delete.label_tag }} -
{{ form.desc.errors }}
Due date:{{ form.due_month }} {{ form.due_year }} {{ form.due_month.errors }} {{ form.due_year.errors }} - {{ form.resolved_checkbox }} {{ form.resolved_checkbox.label_tag }} {{ form.resolved }} - {{ form.resolved.errors }} -
Drafts:{{ form.docs }} - {{ form.docs.errors }} -
Review: - This milestone is not active yet, awaiting - {{ reviewer }} acceptance{% if needs_review %}.{% else %}: {{ form.accept }}{% endif %} -
+{% load bootstrap3 %} + +
+ + + {% bootstrap_form form layout='horizontal' %} +
diff --git a/ietf/templates/group/stream_edit.html b/ietf/templates/group/stream_edit.html index 275810475..0d9e00af8 100644 --- a/ietf/templates/group/stream_edit.html +++ b/ietf/templates/group/stream_edit.html @@ -6,8 +6,8 @@ {% block title %}Manage {{ group.name }} RFC stream{% endblock %} {% block pagehead %} - - + + {% endblock %} {% block content %} @@ -29,12 +29,12 @@ datatracker account. New accounts can be created here.

-
+ {% csrf_token %} {% bootstrap_form form %} {% buttons %} - + Back {% endbuttons %} @@ -42,6 +42,5 @@ {% endblock %} {% block js %} - - + {% endblock %} diff --git a/ietf/templates/nomcom/edit_position.html b/ietf/templates/nomcom/edit_position.html index 72421c573..d536a39f6 100644 --- a/ietf/templates/nomcom/edit_position.html +++ b/ietf/templates/nomcom/edit_position.html @@ -1,8 +1,8 @@ {% extends "nomcom/nomcom_private_base.html" %} {% block pagehead %} - - + + {% endblock %} {% load bootstrap3 %} @@ -27,6 +27,5 @@ {% endblock %} {% block content_end %} - - + {% endblock %} diff --git a/static/facelift/css/ietf.css b/static/facelift/css/ietf.css index be24540b3..6c0f4751a 100644 --- a/static/facelift/css/ietf.css +++ b/static/facelift/css/ietf.css @@ -206,3 +206,17 @@ pre { line-height: 1.214; } .navbar-dev .navbar-link:hover { color: #ecdbff; } + +/* milestone editing */ +#milestones-form .milestone { + cursor: pointer; +} +#milestones-form .milestone:hover { + background-color: #f5f5f5; +} +#milestones-form .edit-milestone { + display: none; +} +#milestones-form .milestone.delete, #milestones-form .edit-milestone.delete, #milestones-form .edit-milestone.delete input { + color: #aaa !important; +} diff --git a/static/facelift/css/lib/select2-bootstrap.css b/static/facelift/css/lib/select2-bootstrap.css new file mode 100644 index 000000000..90a1063a2 --- /dev/null +++ b/static/facelift/css/lib/select2-bootstrap.css @@ -0,0 +1,497 @@ +/** + * Select2 Bootstrap 3 CSS v1.4.1 + * Tested with Bootstrap v3.2.0 and Select2 v3.3.2, v3.4.1-v3.4.5, v3.5.1, master + * in latest Chrome, Safari, Firefox, Opera (Mac) and IE8-IE11 + * MIT License + */ +/** + * Reset Bootstrap 3 .form-control styles which - if applied to the + * original element Select2 is replacing not be properly being hidden + * when used in a "Bootstrap Input Group with Addon". + **/ +.select2-offscreen, +.select2-offscreen:focus { + width: 1px !important; + height: 1px !important; + position: absolute !important; +} diff --git a/static/facelift/css/lib/select2-spinner.gif b/static/facelift/css/lib/select2-spinner.gif new file mode 100644 index 0000000000000000000000000000000000000000..5b33f7e54f4e55b6b8774d86d96895db9af044b4 GIT binary patch literal 1849 zcma*odr(tX9tZI2z31lM+(&YVk%mZ}5P~KlG2s=WSbGzm0!x7^P##Mnh7t-jP!X0Q zk_SQ}Po-L1tlDK;6l?(>v)e5ZBQx4|Y-Q?nr@Px3?9h(3ZWr3^tj=`TP57gKr87N$ zp2wWee1GRRCwo_xahnw)5cxNPJbCg2L6DV|6`#+yw6v6!mDS$f9-JvFD^n;GQ&UrZ zzh5jCkByB101O60U0q#p_1BM>Cv-vP?&s4@g_((4_1L=L$(a91)0=J91Gas#R{McE znYG^9*0A5YZ>#;~+Wkn(W5B0^yELIYLP!K}mB~<)AM@1&nqekynuaEGqPrzoH|KodRXJy)%+w_fu3nE5>@Bd_b zqC$EQ;{c`T&?EsNO|igL9gC7Ygxv?aQUEXMq?~>wg{EyW;VcJ37CUF#HjrT=KQO_* zS>M9yydXk18D(+QDJ1>r);Lav_uYKp$T?4vr{Q$lTo&pKv^?(>L-)G2*lwH!Ah7k? z7oH<8h-(KTKt5V6$8gF)C7Io&P5=SjTh)=zV=E2EUhQZP##L8S{d%UK>>+y82>+FV+#^BzW7u3F)Bb>=lYQ%%j`F>ASe zo*cw@V#u6T`A2He;70mR(V&iV&-7{qP~=SRf&jm9-T{*ZeZ}$rd0#6c&fLG^xJcf5 z+p<`wJYgW+_s*V{uI$nMB;%8`S_3>PfGOj3Rq}@Cx^+j?rk92fANSFDBYnOqQ>Vdj z)(|$AhP4t&Lb=Gvo2#3Gl%9<=Gv`Mz?Po@P4iLF!x}GUWJICDlFk-hS^Whyh7x~VH z@0vD1>HYD4&e+~yzS*-sFR{9`{QEEZO1zg7>R&7cHts-6j!xHVdA8eI+ZlVzd%`es zJT@$#GX(gvCJ1oJN%yLBK}{V=V;seo;!w|Yte!W1%5qLNFWqvZW>h&IiH+oPT=b@E zPhGzv5=(Un*X>v`>%8h_nj^NdYcE6NHS_ifkCV$*D)Tqrbu`s;<=t<4 zAHNqNV?6(g<1PY-w@#I-WYFViz?9TrkMr)u0g`O`u|>T;k|2sV*YF^punvT;$SuTy{j3Gv)yqD!R_CF>yR)MzmmYS5v+~R zXAdD%ng9?df;wd8GxR#%3O+gz};Vo;)sK%Bj-q>Oq%R7JU-KD?vYu>#2UjaDo z&8$>5xW~?KPD_#XFToU1hIb*VOMidUr6iYiO0N|i-7s`T8!cFT`rN!^1Pt78J93i6 z5HI1wIM$94m{3SLDvISDe6$ZG1;eq_D9RTaaC>=cO{@Bs>$IlPCPJJ$h$)-3vzNUQ6OsN#_zWxey!_9%hxwH2_dEJi=yY|1c7nDm2_Lm!Cof8-R_+9UkS zcBE(o47yE)oMR(Q=dp1a2wTX5KvvGyLqlWTa7V&!A*|w|)ax~1_~aJ0=_Lilg*0iQk7#ZD EAHN$8j{pDw literal 0 HcmV?d00001 diff --git a/static/facelift/css/lib/select2.css b/static/facelift/css/lib/select2.css new file mode 100644 index 000000000..2d07a0343 --- /dev/null +++ b/static/facelift/css/lib/select2.css @@ -0,0 +1,704 @@ +/* +Version: 3.5.2 Timestamp: Sat Nov 1 14:43:36 EDT 2014 +*/ +.select2-container { + margin: 0; + position: relative; + display: inline-block; + /* inline-block for ie7 */ + zoom: 1; + *display: inline; + vertical-align: middle; +} + +.select2-container, +.select2-drop, +.select2-search, +.select2-search input { + /* + Force border-box so that % widths fit the parent + container without overlap because of margin/padding. + More Info : http://www.quirksmode.org/css/box.html + */ + -webkit-box-sizing: border-box; /* webkit */ + -moz-box-sizing: border-box; /* firefox */ + box-sizing: border-box; /* css3 */ +} + +.select2-container .select2-choice { + display: block; + height: 26px; + padding: 0 0 0 8px; + overflow: hidden; + position: relative; + + border: 1px solid #aaa; + white-space: nowrap; + line-height: 26px; + color: #444; + text-decoration: none; + + border-radius: 4px; + + background-clip: padding-box; + + -webkit-touch-callout: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + + background-color: #fff; + background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #eee), color-stop(0.5, #fff)); + background-image: -webkit-linear-gradient(center bottom, #eee 0%, #fff 50%); + background-image: -moz-linear-gradient(center bottom, #eee 0%, #fff 50%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr = '#ffffff', endColorstr = '#eeeeee', GradientType = 0); + background-image: linear-gradient(to top, #eee 0%, #fff 50%); +} + +html[dir="rtl"] .select2-container .select2-choice { + padding: 0 8px 0 0; +} + +.select2-container.select2-drop-above .select2-choice { + border-bottom-color: #aaa; + + border-radius: 0 0 4px 4px; + + background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #eee), color-stop(0.9, #fff)); + background-image: -webkit-linear-gradient(center bottom, #eee 0%, #fff 90%); + background-image: -moz-linear-gradient(center bottom, #eee 0%, #fff 90%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#eeeeee', GradientType=0); + background-image: linear-gradient(to bottom, #eee 0%, #fff 90%); +} + +.select2-container.select2-allowclear .select2-choice .select2-chosen { + margin-right: 42px; +} + +.select2-container .select2-choice > .select2-chosen { + margin-right: 26px; + display: block; + overflow: hidden; + + white-space: nowrap; + + text-overflow: ellipsis; + float: none; + width: auto; +} + +html[dir="rtl"] .select2-container .select2-choice > .select2-chosen { + margin-left: 26px; + margin-right: 0; +} + +.select2-container .select2-choice abbr { + display: none; + width: 12px; + height: 12px; + position: absolute; + right: 24px; + top: 8px; + + font-size: 1px; + text-decoration: none; + + border: 0; + background: url('select2.png') right top no-repeat; + cursor: pointer; + outline: 0; +} + +.select2-container.select2-allowclear .select2-choice abbr { + display: inline-block; +} + +.select2-container .select2-choice abbr:hover { + background-position: right -11px; + cursor: pointer; +} + +.select2-drop-mask { + border: 0; + margin: 0; + padding: 0; + position: fixed; + left: 0; + top: 0; + min-height: 100%; + min-width: 100%; + height: auto; + width: auto; + opacity: 0; + z-index: 9998; + /* styles required for IE to work */ + background-color: #fff; + filter: alpha(opacity=0); +} + +.select2-drop { + width: 100%; + margin-top: -1px; + position: absolute; + z-index: 9999; + top: 100%; + + background: #fff; + color: #000; + border: 1px solid #aaa; + border-top: 0; + + border-radius: 0 0 4px 4px; + + -webkit-box-shadow: 0 4px 5px rgba(0, 0, 0, .15); + box-shadow: 0 4px 5px rgba(0, 0, 0, .15); +} + +.select2-drop.select2-drop-above { + margin-top: 1px; + border-top: 1px solid #aaa; + border-bottom: 0; + + border-radius: 4px 4px 0 0; + + -webkit-box-shadow: 0 -4px 5px rgba(0, 0, 0, .15); + box-shadow: 0 -4px 5px rgba(0, 0, 0, .15); +} + +.select2-drop-active { + border: 1px solid #5897fb; + border-top: none; +} + +.select2-drop.select2-drop-above.select2-drop-active { + border-top: 1px solid #5897fb; +} + +.select2-drop-auto-width { + border-top: 1px solid #aaa; + width: auto; +} + +.select2-drop-auto-width .select2-search { + padding-top: 4px; +} + +.select2-container .select2-choice .select2-arrow { + display: inline-block; + width: 18px; + height: 100%; + position: absolute; + right: 0; + top: 0; + + border-left: 1px solid #aaa; + border-radius: 0 4px 4px 0; + + background-clip: padding-box; + + background: #ccc; + background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #ccc), color-stop(0.6, #eee)); + background-image: -webkit-linear-gradient(center bottom, #ccc 0%, #eee 60%); + background-image: -moz-linear-gradient(center bottom, #ccc 0%, #eee 60%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr = '#eeeeee', endColorstr = '#cccccc', GradientType = 0); + background-image: linear-gradient(to top, #ccc 0%, #eee 60%); +} + +html[dir="rtl"] .select2-container .select2-choice .select2-arrow { + left: 0; + right: auto; + + border-left: none; + border-right: 1px solid #aaa; + border-radius: 4px 0 0 4px; +} + +.select2-container .select2-choice .select2-arrow b { + display: block; + width: 100%; + height: 100%; + background: url('select2.png') no-repeat 0 1px; +} + +html[dir="rtl"] .select2-container .select2-choice .select2-arrow b { + background-position: 2px 1px; +} + +.select2-search { + display: inline-block; + width: 100%; + min-height: 26px; + margin: 0; + padding-left: 4px; + padding-right: 4px; + + position: relative; + z-index: 10000; + + white-space: nowrap; +} + +.select2-search input { + width: 100%; + height: auto !important; + min-height: 26px; + padding: 4px 20px 4px 5px; + margin: 0; + + outline: 0; + font-family: sans-serif; + font-size: 1em; + + border: 1px solid #aaa; + border-radius: 0; + + -webkit-box-shadow: none; + box-shadow: none; + + background: #fff url('select2.png') no-repeat 100% -22px; + background: url('select2.png') no-repeat 100% -22px, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, #fff), color-stop(0.99, #eee)); + background: url('select2.png') no-repeat 100% -22px, -webkit-linear-gradient(center bottom, #fff 85%, #eee 99%); + background: url('select2.png') no-repeat 100% -22px, -moz-linear-gradient(center bottom, #fff 85%, #eee 99%); + background: url('select2.png') no-repeat 100% -22px, linear-gradient(to bottom, #fff 85%, #eee 99%) 0 0; +} + +html[dir="rtl"] .select2-search input { + padding: 4px 5px 4px 20px; + + background: #fff url('select2.png') no-repeat -37px -22px; + background: url('select2.png') no-repeat -37px -22px, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, #fff), color-stop(0.99, #eee)); + background: url('select2.png') no-repeat -37px -22px, -webkit-linear-gradient(center bottom, #fff 85%, #eee 99%); + background: url('select2.png') no-repeat -37px -22px, -moz-linear-gradient(center bottom, #fff 85%, #eee 99%); + background: url('select2.png') no-repeat -37px -22px, linear-gradient(to bottom, #fff 85%, #eee 99%) 0 0; +} + +.select2-drop.select2-drop-above .select2-search input { + margin-top: 4px; +} + +.select2-search input.select2-active { + background: #fff url('select2-spinner.gif') no-repeat 100%; + background: url('select2-spinner.gif') no-repeat 100%, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, #fff), color-stop(0.99, #eee)); + background: url('select2-spinner.gif') no-repeat 100%, -webkit-linear-gradient(center bottom, #fff 85%, #eee 99%); + background: url('select2-spinner.gif') no-repeat 100%, -moz-linear-gradient(center bottom, #fff 85%, #eee 99%); + background: url('select2-spinner.gif') no-repeat 100%, linear-gradient(to bottom, #fff 85%, #eee 99%) 0 0; +} + +.select2-container-active .select2-choice, +.select2-container-active .select2-choices { + border: 1px solid #5897fb; + outline: none; + + -webkit-box-shadow: 0 0 5px rgba(0, 0, 0, .3); + box-shadow: 0 0 5px rgba(0, 0, 0, .3); +} + +.select2-dropdown-open .select2-choice { + border-bottom-color: transparent; + -webkit-box-shadow: 0 1px 0 #fff inset; + box-shadow: 0 1px 0 #fff inset; + + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + + background-color: #eee; + background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #fff), color-stop(0.5, #eee)); + background-image: -webkit-linear-gradient(center bottom, #fff 0%, #eee 50%); + background-image: -moz-linear-gradient(center bottom, #fff 0%, #eee 50%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#ffffff', GradientType=0); + background-image: linear-gradient(to top, #fff 0%, #eee 50%); +} + +.select2-dropdown-open.select2-drop-above .select2-choice, +.select2-dropdown-open.select2-drop-above .select2-choices { + border: 1px solid #5897fb; + border-top-color: transparent; + + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #fff), color-stop(0.5, #eee)); + background-image: -webkit-linear-gradient(center top, #fff 0%, #eee 50%); + background-image: -moz-linear-gradient(center top, #fff 0%, #eee 50%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#ffffff', GradientType=0); + background-image: linear-gradient(to bottom, #fff 0%, #eee 50%); +} + +.select2-dropdown-open .select2-choice .select2-arrow { + background: transparent; + border-left: none; + filter: none; +} +html[dir="rtl"] .select2-dropdown-open .select2-choice .select2-arrow { + border-right: none; +} + +.select2-dropdown-open .select2-choice .select2-arrow b { + background-position: -18px 1px; +} + +html[dir="rtl"] .select2-dropdown-open .select2-choice .select2-arrow b { + background-position: -16px 1px; +} + +.select2-hidden-accessible { + border: 0; + clip: rect(0 0 0 0); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; +} + +/* results */ +.select2-results { + max-height: 200px; + padding: 0 0 0 4px; + margin: 4px 4px 4px 0; + position: relative; + overflow-x: hidden; + overflow-y: auto; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); +} + +html[dir="rtl"] .select2-results { + padding: 0 4px 0 0; + margin: 4px 0 4px 4px; +} + +.select2-results ul.select2-result-sub { + margin: 0; + padding-left: 0; +} + +.select2-results li { + list-style: none; + display: list-item; + background-image: none; +} + +.select2-results li.select2-result-with-children > .select2-result-label { + font-weight: bold; +} + +.select2-results .select2-result-label { + padding: 3px 7px 4px; + margin: 0; + cursor: pointer; + + min-height: 1em; + + -webkit-touch-callout: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.select2-results-dept-1 .select2-result-label { padding-left: 20px } +.select2-results-dept-2 .select2-result-label { padding-left: 40px } +.select2-results-dept-3 .select2-result-label { padding-left: 60px } +.select2-results-dept-4 .select2-result-label { padding-left: 80px } +.select2-results-dept-5 .select2-result-label { padding-left: 100px } +.select2-results-dept-6 .select2-result-label { padding-left: 110px } +.select2-results-dept-7 .select2-result-label { padding-left: 120px } + +.select2-results .select2-highlighted { + background: #3875d7; + color: #fff; +} + +.select2-results li em { + background: #feffde; + font-style: normal; +} + +.select2-results .select2-highlighted em { + background: transparent; +} + +.select2-results .select2-highlighted ul { + background: #fff; + color: #000; +} + +.select2-results .select2-no-results, +.select2-results .select2-searching, +.select2-results .select2-ajax-error, +.select2-results .select2-selection-limit { + background: #f4f4f4; + display: list-item; + padding-left: 5px; +} + +/* +disabled look for disabled choices in the results dropdown +*/ +.select2-results .select2-disabled.select2-highlighted { + color: #666; + background: #f4f4f4; + display: list-item; + cursor: default; +} +.select2-results .select2-disabled { + background: #f4f4f4; + display: list-item; + cursor: default; +} + +.select2-results .select2-selected { + display: none; +} + +.select2-more-results.select2-active { + background: #f4f4f4 url('select2-spinner.gif') no-repeat 100%; +} + +.select2-results .select2-ajax-error { + background: rgba(255, 50, 50, .2); +} + +.select2-more-results { + background: #f4f4f4; + display: list-item; +} + +/* disabled styles */ + +.select2-container.select2-container-disabled .select2-choice { + background-color: #f4f4f4; + background-image: none; + border: 1px solid #ddd; + cursor: default; +} + +.select2-container.select2-container-disabled .select2-choice .select2-arrow { + background-color: #f4f4f4; + background-image: none; + border-left: 0; +} + +.select2-container.select2-container-disabled .select2-choice abbr { + display: none; +} + + +/* multiselect */ + +.select2-container-multi .select2-choices { + height: auto !important; + height: 1%; + margin: 0; + padding: 0 5px 0 0; + position: relative; + + border: 1px solid #aaa; + cursor: text; + overflow: hidden; + + background-color: #fff; + background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(1%, #eee), color-stop(15%, #fff)); + background-image: -webkit-linear-gradient(top, #eee 1%, #fff 15%); + background-image: -moz-linear-gradient(top, #eee 1%, #fff 15%); + background-image: linear-gradient(to bottom, #eee 1%, #fff 15%); +} + +html[dir="rtl"] .select2-container-multi .select2-choices { + padding: 0 0 0 5px; +} + +.select2-locked { + padding: 3px 5px 3px 5px !important; +} + +.select2-container-multi .select2-choices { + min-height: 26px; +} + +.select2-container-multi.select2-container-active .select2-choices { + border: 1px solid #5897fb; + outline: none; + + -webkit-box-shadow: 0 0 5px rgba(0, 0, 0, .3); + box-shadow: 0 0 5px rgba(0, 0, 0, .3); +} +.select2-container-multi .select2-choices li { + float: left; + list-style: none; +} +html[dir="rtl"] .select2-container-multi .select2-choices li +{ + float: right; +} +.select2-container-multi .select2-choices .select2-search-field { + margin: 0; + padding: 0; + white-space: nowrap; +} + +.select2-container-multi .select2-choices .select2-search-field input { + padding: 5px; + margin: 1px 0; + + font-family: sans-serif; + font-size: 100%; + color: #666; + outline: 0; + border: 0; + -webkit-box-shadow: none; + box-shadow: none; + background: transparent !important; +} + +.select2-container-multi .select2-choices .select2-search-field input.select2-active { + background: #fff url('select2-spinner.gif') no-repeat 100% !important; +} + +.select2-default { + color: #999 !important; +} + +.select2-container-multi .select2-choices .select2-search-choice { + padding: 3px 5px 3px 18px; + margin: 3px 0 3px 5px; + position: relative; + + line-height: 13px; + color: #333; + cursor: default; + border: 1px solid #aaaaaa; + + border-radius: 3px; + + -webkit-box-shadow: 0 0 2px #fff inset, 0 1px 0 rgba(0, 0, 0, 0.05); + box-shadow: 0 0 2px #fff inset, 0 1px 0 rgba(0, 0, 0, 0.05); + + background-clip: padding-box; + + -webkit-touch-callout: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + + background-color: #e4e4e4; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#f4f4f4', GradientType=0); + background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), color-stop(100%, #eee)); + background-image: -webkit-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eee 100%); + background-image: -moz-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eee 100%); + background-image: linear-gradient(to bottom, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eee 100%); +} +html[dir="rtl"] .select2-container-multi .select2-choices .select2-search-choice +{ + margin: 3px 5px 3px 0; + padding: 3px 18px 3px 5px; +} +.select2-container-multi .select2-choices .select2-search-choice .select2-chosen { + cursor: default; +} +.select2-container-multi .select2-choices .select2-search-choice-focus { + background: #d4d4d4; +} + +.select2-search-choice-close { + display: block; + width: 12px; + height: 13px; + position: absolute; + right: 3px; + top: 4px; + + font-size: 1px; + outline: none; + background: url('select2.png') right top no-repeat; +} +html[dir="rtl"] .select2-search-choice-close { + right: auto; + left: 3px; +} + +.select2-container-multi .select2-search-choice-close { + left: 3px; +} + +html[dir="rtl"] .select2-container-multi .select2-search-choice-close { + left: auto; + right: 2px; +} + +.select2-container-multi .select2-choices .select2-search-choice .select2-search-choice-close:hover { + background-position: right -11px; +} +.select2-container-multi .select2-choices .select2-search-choice-focus .select2-search-choice-close { + background-position: right -11px; +} + +/* disabled styles */ +.select2-container-multi.select2-container-disabled .select2-choices { + background-color: #f4f4f4; + background-image: none; + border: 1px solid #ddd; + cursor: default; +} + +.select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice { + padding: 3px 5px 3px 5px; + border: 1px solid #ddd; + background-image: none; + background-color: #f4f4f4; +} + +.select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice .select2-search-choice-close { display: none; + background: none; +} +/* end multiselect */ + + +.select2-result-selectable .select2-match, +.select2-result-unselectable .select2-match { + text-decoration: underline; +} + +.select2-offscreen, .select2-offscreen:focus { + clip: rect(0 0 0 0) !important; + width: 1px !important; + height: 1px !important; + border: 0 !important; + margin: 0 !important; + padding: 0 !important; + overflow: hidden !important; + position: absolute !important; + outline: 0 !important; + left: 0px !important; + top: 0px !important; +} + +.select2-display-none { + display: none; +} + +.select2-measure-scrollbar { + position: absolute; + top: -10000px; + left: -10000px; + width: 100px; + height: 100px; + overflow: scroll; +} + +/* Retina-ize icons */ + +@media only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-resolution: 2dppx) { + .select2-search input, + .select2-search-choice-close, + .select2-container .select2-choice abbr, + .select2-container .select2-choice .select2-arrow b { + background-image: url('select2x2.png') !important; + background-repeat: no-repeat !important; + background-size: 60px 40px !important; + } + + .select2-search input { + background-position: 100% -21px !important; + } +} diff --git a/static/facelift/css/lib/select2.png b/static/facelift/css/lib/select2.png new file mode 100644 index 0000000000000000000000000000000000000000..1d804ffb99699b9e030f1010314de0970b5a000d GIT binary patch literal 613 zcmV-r0-F7aP)#WY!I$JQV$)A5aAS1BM||2XVJl=+L1^1S1H% zM-&lx?NZpUrHhn>fk<>POqf2sh40}xxGZfc+t+#Eb(qHy9_3*1(U%t9t)QDnI#YAL(|ACV(>)>6WD-t!8tutHkdb^#3`HzoJG3A2@T`% zA|K@o*b!`R#(7)PWrMFn2))Ca3MR4(zaT`Zr61*kZK5NPnZwQszxh$fyv3?&4c>$q z2m=+yc0dRXRAsPDxF6sD;@rK4JGdR_``1S~o6Xi@2&aR6hcSrEp9HVRzEqVDqBn<1%hR=D4e1f^ra^A|34Cjc=Gny{F(o#MrvPYgZuTJOz(n)-F<| zj()qR;C={)N<0RRvDZ^@6ND+W*}gh-Lip(MDt!(zMSO)!j2j+*hxgzC-e3$@(O2p* zu;+gddm(cZwXTCLx*Ky4THOa*^b^F`woveIeCK^0aR|TJ00000NkvXXu0mjfA#WC6 literal 0 HcmV?d00001 diff --git a/static/facelift/js/edit-milestones.js b/static/facelift/js/edit-milestones.js new file mode 100644 index 000000000..c73a5d4c2 --- /dev/null +++ b/static/facelift/js/edit-milestones.js @@ -0,0 +1,140 @@ +$(document).ready(function () { + var idCounter = -1; + var milestonesForm = $('#milestones-form'); + + // make sure we got the lowest number for idCounter + milestonesForm.find('.edit-milestone input[name$="-id"]').each(function () { + var v = +this.value; + if (!isNaN(v) && v < idCounter) + idCounter = v - 1; + }); + + function setChanged() { + $(this).closest(".edit-milestone").addClass("changed"); + setSubmitButtonState(); + } + + milestonesForm.on("change", '.edit-milestone select,.edit-milestone input,.edit-milestone textarea', setChanged); + milestonesForm.on("click", '.edit-milestone .select2 input', setChanged); + + // the required stuff seems to trip up many browsers with dynamic forms + milestonesForm.find("input").prop("required", false); + + + function setSubmitButtonState() { + var action, label; + if (milestonesForm.find("input[name$=delete]:visible").length > 0) + action = "review"; + else + action = "save"; + + milestonesForm.find("input[name=action]").val(action); + + var submit = milestonesForm.find("[type=submit]"); + submit.text(submit.data("label" + action)); + if (milestonesForm.find(".edit-milestone.changed").length > 0 || action == "review") + submit.show(); + else + submit.hide(); + } + + milestonesForm.find(".milestone").click(function () { + var row = $(this), editRow = row.next(".edit-milestone"); + row.hide(); + editRow.show(); + + editRow.find('input[name$="desc"]').focus(); + + setSubmitButtonState(); + + // collapse unchanged rows + milestonesForm.find(".milestone").not(this).each(function () { + var e = $(this).next('.edit-milestone'); + if (e.is(":visible") && !e.hasClass("changed")) { + $(this).show(); + e.hide(); + } + }); + }); + + milestonesForm.find(".add-milestone").click(function() { + // move Add milestone row and duplicate hidden template + var row = $(this).closest("tr"), editRow = row.next(".edit-milestone"); + row.closest("table").append(row).append(editRow.clone()); + + // fixup template + var newId = idCounter; + --idCounter; + + var prefix = "m" + newId; + editRow.find('input[name="prefix"]').val(prefix); + + editRow.find("input,select,textarea").each(function () { + if (this.name == "prefix") + return; + + if (this.name == "id") + this.value = "" + idCounter; + + this.name = prefix + "-" + this.name; + this.id = prefix + "-" + this.id; + }); + editRow.find("label").each(function () { + if (this.htmlFor) + this.htmlFor = prefix + "-" + this.htmlFor; + }); + + editRow.removeClass("template"); + editRow.show(); + + editRow.find(".select2-field").each(function () { + window.setupSelect2Field($(this)); // from ietf.js + }); + }); + + function setResolvedState() { + var resolved = $(this).is(":checked"); + var label = $(this).closest(".edit-milestone").find("label[for=" + this.id + "]"); + var reason = $(this).closest(".edit-milestone").find("[name$=resolved]"); + if (resolved) { + reason.closest(".form-group").show(); + if (!reason.val()) + reason.val(reason.data("default")); + } + else { + reason.closest(".form-group").hide(); + reason.val(""); + } + } + + milestonesForm.find(".edit-milestone [name$=resolved_checkbox]").each(setResolvedState); + milestonesForm.on("change", ".edit-milestone [name$=resolved_checkbox]", setResolvedState); + + function setDeleteState() { + var edit = $(this).closest(".edit-milestone"), row = edit.prev(".milestone"); + + if ($(this).is(":checked")) { + if (+edit.find('input[name$="id"]').val() < 0) { + edit.remove(); + setSubmitButtonState(); + } + else { + row.addClass("delete"); + edit.addClass("delete"); + } + } + else { + row.removeClass("delete"); + edit.removeClass("delete"); + } + } + + milestonesForm.find(".edit-milestone [name$=delete]").each(setDeleteState); + milestonesForm.on("change", ".edit-milestone input[name$=delete]", setDeleteState); + + milestonesForm.find('.edit-milestone .has-error').each(function () { + $(this).closest(".edit-milestone").prev().click(); + }); + + setSubmitButtonState(); +}); diff --git a/static/facelift/js/ietf.js b/static/facelift/js/ietf.js index bc956b126..4b261703d 100644 --- a/static/facelift/js/ietf.js +++ b/static/facelift/js/ietf.js @@ -192,160 +192,59 @@ $(".snippet .show-all").click(function () { // } // }); +function setupSelect2Field(e) { + var url = e.data("ajax-url"); + if (!url) + return; -function to_disp(t) { - // typehead/tokenfield don't fully deal with HTML entities - return $('
').html(t).text().replace(/[<>"]/g, function (m) { - return { - '<': '(', - '>': ')', - '"': '' - }[m]; - }); + var maxEntries = e.data("max-entries"); + var multiple = maxEntries != 1; + var prefetched = e.data("pre"); + e.select2({ + multiple: multiple, + minimumInputLength: 2, + width: "off", + allowClear: true, + maximumSelectionSize: maxEntries, + ajax: { + url: url, + dataType: "json", + quietMillis: 250, + data: function (term, page) { + return { + q: term, + p: page + }; + }, + results: function (results) { + return { + results: results, + more: results.length == 10 + }; + } + }, + escapeMarkup: function (m) { + return m; + }, + initSelection: function (element, cb) { + if (!multiple && prefetched.length > 0) + cb(prefetched[0]); + else + cb(prefetched); + + }, + dropdownCssClass: "bigdrop" + }); } +$(document).ready(function () { + $(".select2-field").each(function () { + if ($(this).closest(".template").length > 0) + return; -$(".tokenized-form").submit(function (e) { - $(this).find(".tokenized-field").each(function () { - var f = $(this); - var io = f.data("io"); - var format = f.data("format"); - var t = f.tokenfield("getTokens"); - - var v = $.map(t, function(o) { return o["value"]; }); - if (format === "json") { - v = JSON.stringify(v); - } else if (format === "csv") { - v = v.join(", "); - } else { - console.log(io, "unknown format"); - v = v.join(" "); - } - f.val(v); - if (io) { - $(io).val(v); - } - }); -}); - - -$(".tokenized-field").each(function () { - // autocomplete interferes with the token popup - $(this).attr("autocomplete", "off"); - - // in which field ID are we expected to place the result - // (we also read the prefill information from there) - var io = $(this).data("io"); - var raw = ""; - if (io) { - raw = $(io).val(); - } else { - io = "#" + this.id; - raw = $(this).val(); - } - console.log("io: ", io); - console.log(io, "raw", raw); - $(this).data("io", io); - - // which field of the JSON are we supposed to display - var display = $(this).data("display"); - if (!display) { - display = "name"; - } - console.log(io, "display", display); - $(this).data("display", display); - - // which field of the JSON are we supposed to return - var result = $(this).data("result"); - if (!result) { - result = "id"; - } - console.log(io, "result", result); - $(this).data("result", result); - - // what kind of data are we returning (json or csv) - var format = $(this).data("format"); - if (!format) { - format = "csv"; - } - console.log(io, "format", format); - $(this).data("format", format); - - // make tokens to prefill the input - if (raw) { - raw = $.parseJSON(raw); - var pre = []; - if (!raw[0] || !raw[0][display]) { - $.each(raw, function(k, v) { - var obj = {}; - obj["value"] = k; - obj["label"] = to_disp(v); - pre.push(obj); - }); - } else { - for (var i in raw) { - var obj = {}; - obj["value"] = raw[i][result]; - obj["label"] = to_disp(raw[i][display]); - pre.push(obj); - } - } - $(this).val(pre); - } - console.log(io, "pre", pre); - - // check if the ajax-url contains a query parameter, add one if not - var url = $(this).data("ajax-url"); - if (url.indexOf("?") === -1) { - url += "?q="; - } - $(this).data("ajax-url", url); - console.log(io, "ajax-url", url); - - var bh = new Bloodhound({ - datumTokenizer: function (d) { - return Bloodhound.tokenizers.nonword(d[display]); - }, - queryTokenizer: Bloodhound.tokenizers.nonword, - limit: 20, - remote: { - url: url + "%QUERY", - filter: function (data) { - return $.map($.grep(data, function (n, i) { - return true; - }), function (n, i) { - n["label"] = to_disp(n[display]); - n["value"] = n[result]; - return n; - }); - } - } - }); - bh.initialize(); - $(this).tokenfield({ - typeahead: [{ - highlight: true, - minLength: 3, - hint: true - }, { - source: bh.ttAdapter(), - displayKey: "label" - }], - beautify: true, - delimiter: [',', ';'] - }).tokenfield("setTokens", pre); - - // only allow tokens from the popup to be added to the field, no free text - $(this).on('tokenfield:createtoken', function (event) { - var existingTokens = $(this).tokenfield('getTokens'); - $.each(existingTokens, function(index, token) { - if (event.attrs.id === undefined) { - event.preventDefault(); - } - }); + setupSelect2Field($(this)); }); }); - // Use the Bootstrap3 tooltip plugin for all elements with a title attribute $('[title][title!=""]').tooltip(); diff --git a/static/facelift/js/lib/select2-3.5.2.min.js b/static/facelift/js/lib/select2-3.5.2.min.js new file mode 100644 index 000000000..b56419e2e --- /dev/null +++ b/static/facelift/js/lib/select2-3.5.2.min.js @@ -0,0 +1,23 @@ +/* +Copyright 2014 Igor Vaynberg + +Version: 3.5.2 Timestamp: Sat Nov 1 14:43:36 EDT 2014 + +This software is licensed under the Apache License, Version 2.0 (the "Apache License") or the GNU +General Public License version 2 (the "GPL License"). You may choose either license to govern your +use of this software only upon the condition that you accept all of the terms of either the Apache +License or the GPL License. + +You may obtain a copy of the Apache License and the GPL License at: + +http://www.apache.org/licenses/LICENSE-2.0 +http://www.gnu.org/licenses/gpl-2.0.html + +Unless required by applicable law or agreed to in writing, software distributed under the Apache License +or the GPL Licesnse is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +either express or implied. See the Apache License and the GPL License for the specific language governing +permissions and limitations under the Apache License and the GPL License. +*/ +!function(a){"undefined"==typeof a.fn.each2&&a.extend(a.fn,{each2:function(b){for(var c=a([0]),d=-1,e=this.length;++dc;c+=1)if(r(a,b[c]))return c;return-1}function q(){var b=a(l);b.appendTo(document.body);var c={width:b.width()-b[0].clientWidth,height:b.height()-b[0].clientHeight};return b.remove(),c}function r(a,c){return a===c?!0:a===b||c===b?!1:null===a||null===c?!1:a.constructor===String?a+""==c+"":c.constructor===String?c+""==a+"":!1}function s(a,b,c){var d,e,f;if(null===a||a.length<1)return[];for(d=a.split(b),e=0,f=d.length;f>e;e+=1)d[e]=c(d[e]);return d}function t(a){return a.outerWidth(!1)-a.width()}function u(c){var d="keyup-change-value";c.on("keydown",function(){a.data(c,d)===b&&a.data(c,d,c.val())}),c.on("keyup",function(){var e=a.data(c,d);e!==b&&c.val()!==e&&(a.removeData(c,d),c.trigger("keyup-change"))})}function v(c){c.on("mousemove",function(c){var d=h;(d===b||d.x!==c.pageX||d.y!==c.pageY)&&a(c.target).trigger("mousemove-filtered",c)})}function w(a,c,d){d=d||b;var e;return function(){var b=arguments;window.clearTimeout(e),e=window.setTimeout(function(){c.apply(d,b)},a)}}function x(a,b){var c=w(a,function(a){b.trigger("scroll-debounced",a)});b.on("scroll",function(a){p(a.target,b.get())>=0&&c(a)})}function y(a){a[0]!==document.activeElement&&window.setTimeout(function(){var d,b=a[0],c=a.val().length;a.focus();var e=b.offsetWidth>0||b.offsetHeight>0;e&&b===document.activeElement&&(b.setSelectionRange?b.setSelectionRange(c,c):b.createTextRange&&(d=b.createTextRange(),d.collapse(!1),d.select()))},0)}function z(b){b=a(b)[0];var c=0,d=0;if("selectionStart"in b)c=b.selectionStart,d=b.selectionEnd-c;else if("selection"in document){b.focus();var e=document.selection.createRange();d=document.selection.createRange().text.length,e.moveStart("character",-b.value.length),c=e.text.length-d}return{offset:c,length:d}}function A(a){a.preventDefault(),a.stopPropagation()}function B(a){a.preventDefault(),a.stopImmediatePropagation()}function C(b){if(!g){var c=b[0].currentStyle||window.getComputedStyle(b[0],null);g=a(document.createElement("div")).css({position:"absolute",left:"-10000px",top:"-10000px",display:"none",fontSize:c.fontSize,fontFamily:c.fontFamily,fontStyle:c.fontStyle,fontWeight:c.fontWeight,letterSpacing:c.letterSpacing,textTransform:c.textTransform,whiteSpace:"nowrap"}),g.attr("class","select2-sizer"),a(document.body).append(g)}return g.text(b.val()),g.width()}function D(b,c,d){var e,g,f=[];e=a.trim(b.attr("class")),e&&(e=""+e,a(e.split(/\s+/)).each2(function(){0===this.indexOf("select2-")&&f.push(this)})),e=a.trim(c.attr("class")),e&&(e=""+e,a(e.split(/\s+/)).each2(function(){0!==this.indexOf("select2-")&&(g=d(this),g&&f.push(g))})),b.attr("class",f.join(" "))}function E(a,b,c,d){var e=o(a.toUpperCase()).indexOf(o(b.toUpperCase())),f=b.length;return 0>e?(c.push(d(a)),void 0):(c.push(d(a.substring(0,e))),c.push(""),c.push(d(a.substring(e,e+f))),c.push(""),c.push(d(a.substring(e+f,a.length))),void 0)}function F(a){var b={"\\":"\","&":"&","<":"<",">":">",'"':""","'":"'","/":"/"};return String(a).replace(/[&<>"'\/\\]/g,function(a){return b[a]})}function G(c){var d,e=null,f=c.quietMillis||100,g=c.url,h=this;return function(i){window.clearTimeout(d),d=window.setTimeout(function(){var d=c.data,f=g,j=c.transport||a.fn.select2.ajaxDefaults.transport,k={type:c.type||"GET",cache:c.cache||!1,jsonpCallback:c.jsonpCallback||b,dataType:c.dataType||"json"},l=a.extend({},a.fn.select2.ajaxDefaults.params,k);d=d?d.call(h,i.term,i.page,i.context):null,f="function"==typeof f?f.call(h,i.term,i.page,i.context):f,e&&"function"==typeof e.abort&&e.abort(),c.params&&(a.isFunction(c.params)?a.extend(l,c.params.call(h)):a.extend(l,c.params)),a.extend(l,{url:f,dataType:c.dataType,data:d,success:function(a){var b=c.results(a,i.page,i);i.callback(b)},error:function(a,b,c){var d={hasError:!0,jqXHR:a,textStatus:b,errorThrown:c};i.callback(d)}}),e=j.call(h,l)},f)}}function H(b){var d,e,c=b,f=function(a){return""+a.text};a.isArray(c)&&(e=c,c={results:e}),a.isFunction(c)===!1&&(e=c,c=function(){return e});var g=c();return g.text&&(f=g.text,a.isFunction(f)||(d=g.text,f=function(a){return a[d]})),function(b){var g,d=b.term,e={results:[]};return""===d?(b.callback(c()),void 0):(g=function(c,e){var h,i;if(c=c[0],c.children){h={};for(i in c)c.hasOwnProperty(i)&&(h[i]=c[i]);h.children=[],a(c.children).each2(function(a,b){g(b,h.children)}),(h.children.length||b.matcher(d,f(h),c))&&e.push(h)}else b.matcher(d,f(c),c)&&e.push(c)},a(c().results).each2(function(a,b){g(b,e.results)}),b.callback(e),void 0)}}function I(c){var d=a.isFunction(c);return function(e){var f=e.term,g={results:[]},h=d?c(e):c;a.isArray(h)&&(a(h).each(function(){var a=this.text!==b,c=a?this.text:this;(""===f||e.matcher(f,c))&&g.results.push(a?this:{id:this,text:this})}),e.callback(g))}}function J(b,c){if(a.isFunction(b))return!0;if(!b)return!1;if("string"==typeof b)return!0;throw new Error(c+" must be a string, function, or falsy value")}function K(b,c){if(a.isFunction(b)){var d=Array.prototype.slice.call(arguments,2);return b.apply(c,d)}return b}function L(b){var c=0;return a.each(b,function(a,b){b.children?c+=L(b.children):c++}),c}function M(a,c,d,e){var h,i,j,k,l,f=a,g=!1;if(!e.createSearchChoice||!e.tokenSeparators||e.tokenSeparators.length<1)return b;for(;;){for(i=-1,j=0,k=e.tokenSeparators.length;k>j&&(l=e.tokenSeparators[j],i=a.indexOf(l),!(i>=0));j++);if(0>i)break;if(h=a.substring(0,i),a=a.substring(i+l.length),h.length>0&&(h=e.createSearchChoice.call(this,h,c),h!==b&&null!==h&&e.id(h)!==b&&null!==e.id(h))){for(g=!1,j=0,k=c.length;k>j;j++)if(r(e.id(h),e.id(c[j]))){g=!0;break}g||d(h)}}return f!==a?a:void 0}function N(){var b=this;a.each(arguments,function(a,c){b[c].remove(),b[c]=null})}function O(b,c){var d=function(){};return d.prototype=new b,d.prototype.constructor=d,d.prototype.parent=b.prototype,d.prototype=a.extend(d.prototype,c),d}if(window.Select2===b){var c,d,e,f,g,i,j,h={x:0,y:0},k={TAB:9,ENTER:13,ESC:27,SPACE:32,LEFT:37,UP:38,RIGHT:39,DOWN:40,SHIFT:16,CTRL:17,ALT:18,PAGE_UP:33,PAGE_DOWN:34,HOME:36,END:35,BACKSPACE:8,DELETE:46,isArrow:function(a){switch(a=a.which?a.which:a){case k.LEFT:case k.RIGHT:case k.UP:case k.DOWN:return!0}return!1},isControl:function(a){var b=a.which;switch(b){case k.SHIFT:case k.CTRL:case k.ALT:return!0}return a.metaKey?!0:!1},isFunctionKey:function(a){return a=a.which?a.which:a,a>=112&&123>=a}},l="
",m={"\u24b6":"A","\uff21":"A","\xc0":"A","\xc1":"A","\xc2":"A","\u1ea6":"A","\u1ea4":"A","\u1eaa":"A","\u1ea8":"A","\xc3":"A","\u0100":"A","\u0102":"A","\u1eb0":"A","\u1eae":"A","\u1eb4":"A","\u1eb2":"A","\u0226":"A","\u01e0":"A","\xc4":"A","\u01de":"A","\u1ea2":"A","\xc5":"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","\xc6":"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","\xc7":"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","\xc8":"E","\xc9":"E","\xca":"E","\u1ec0":"E","\u1ebe":"E","\u1ec4":"E","\u1ec2":"E","\u1ebc":"E","\u0112":"E","\u1e14":"E","\u1e16":"E","\u0114":"E","\u0116":"E","\xcb":"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","\xcc":"I","\xcd":"I","\xce":"I","\u0128":"I","\u012a":"I","\u012c":"I","\u0130":"I","\xcf":"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","\xd1":"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","\xd2":"O","\xd3":"O","\xd4":"O","\u1ed2":"O","\u1ed0":"O","\u1ed6":"O","\u1ed4":"O","\xd5":"O","\u1e4c":"O","\u022c":"O","\u1e4e":"O","\u014c":"O","\u1e50":"O","\u1e52":"O","\u014e":"O","\u022e":"O","\u0230":"O","\xd6":"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","\xd8":"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","\xd9":"U","\xda":"U","\xdb":"U","\u0168":"U","\u1e78":"U","\u016a":"U","\u1e7a":"U","\u016c":"U","\xdc":"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","\xdd":"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","\xe0":"a","\xe1":"a","\xe2":"a","\u1ea7":"a","\u1ea5":"a","\u1eab":"a","\u1ea9":"a","\xe3":"a","\u0101":"a","\u0103":"a","\u1eb1":"a","\u1eaf":"a","\u1eb5":"a","\u1eb3":"a","\u0227":"a","\u01e1":"a","\xe4":"a","\u01df":"a","\u1ea3":"a","\xe5":"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","\xe6":"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","\xe7":"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","\xe8":"e","\xe9":"e","\xea":"e","\u1ec1":"e","\u1ebf":"e","\u1ec5":"e","\u1ec3":"e","\u1ebd":"e","\u0113":"e","\u1e15":"e","\u1e17":"e","\u0115":"e","\u0117":"e","\xeb":"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","\xec":"i","\xed":"i","\xee":"i","\u0129":"i","\u012b":"i","\u012d":"i","\xef":"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","\xf1":"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","\xf2":"o","\xf3":"o","\xf4":"o","\u1ed3":"o","\u1ed1":"o","\u1ed7":"o","\u1ed5":"o","\xf5":"o","\u1e4d":"o","\u022d":"o","\u1e4f":"o","\u014d":"o","\u1e51":"o","\u1e53":"o","\u014f":"o","\u022f":"o","\u0231":"o","\xf6":"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","\xf8":"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","\xdf":"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","\xf9":"u","\xfa":"u","\xfb":"u","\u0169":"u","\u1e79":"u","\u016b":"u","\u1e7b":"u","\u016d":"u","\xfc":"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","\xfd":"y","\u0177":"y","\u1ef9":"y","\u0233":"y","\u1e8f":"y","\xff":"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"};i=a(document),f=function(){var a=1;return function(){return a++}}(),c=O(Object,{bind:function(a){var b=this;return function(){a.apply(b,arguments)}},init:function(c){var d,e,g=".select2-results";this.opts=c=this.prepareOpts(c),this.id=c.id,c.element.data("select2")!==b&&null!==c.element.data("select2")&&c.element.data("select2").destroy(),this.container=this.createContainer(),this.liveRegion=a(".select2-hidden-accessible"),0==this.liveRegion.length&&(this.liveRegion=a("",{role:"status","aria-live":"polite"}).addClass("select2-hidden-accessible").appendTo(document.body)),this.containerId="s2id_"+(c.element.attr("id")||"autogen"+f()),this.containerEventName=this.containerId.replace(/([.])/g,"_").replace(/([;&,\-\.\+\*\~':"\!\^#$%@\[\]\(\)=>\|])/g,"\\$1"),this.container.attr("id",this.containerId),this.container.attr("title",c.element.attr("title")),this.body=a(document.body),D(this.container,this.opts.element,this.opts.adaptContainerCssClass),this.container.attr("style",c.element.attr("style")),this.container.css(K(c.containerCss,this.opts.element)),this.container.addClass(K(c.containerCssClass,this.opts.element)),this.elementTabIndex=this.opts.element.attr("tabindex"),this.opts.element.data("select2",this).attr("tabindex","-1").before(this.container).on("click.select2",A),this.container.data("select2",this),this.dropdown=this.container.find(".select2-drop"),D(this.dropdown,this.opts.element,this.opts.adaptDropdownCssClass),this.dropdown.addClass(K(c.dropdownCssClass,this.opts.element)),this.dropdown.data("select2",this),this.dropdown.on("click",A),this.results=d=this.container.find(g),this.search=e=this.container.find("input.select2-input"),this.queryCount=0,this.resultsPage=0,this.context=null,this.initContainer(),this.container.on("click",A),v(this.results),this.dropdown.on("mousemove-filtered",g,this.bind(this.highlightUnderEvent)),this.dropdown.on("touchstart touchmove touchend",g,this.bind(function(a){this._touchEvent=!0,this.highlightUnderEvent(a)})),this.dropdown.on("touchmove",g,this.bind(this.touchMoved)),this.dropdown.on("touchstart touchend",g,this.bind(this.clearTouchMoved)),this.dropdown.on("click",this.bind(function(){this._touchEvent&&(this._touchEvent=!1,this.selectHighlighted())})),x(80,this.results),this.dropdown.on("scroll-debounced",g,this.bind(this.loadMoreIfNeeded)),a(this.container).on("change",".select2-input",function(a){a.stopPropagation()}),a(this.dropdown).on("change",".select2-input",function(a){a.stopPropagation()}),a.fn.mousewheel&&d.mousewheel(function(a,b,c,e){var f=d.scrollTop();e>0&&0>=f-e?(d.scrollTop(0),A(a)):0>e&&d.get(0).scrollHeight-d.scrollTop()+e<=d.height()&&(d.scrollTop(d.get(0).scrollHeight-d.height()),A(a))}),u(e),e.on("keyup-change input paste",this.bind(this.updateResults)),e.on("focus",function(){e.addClass("select2-focused")}),e.on("blur",function(){e.removeClass("select2-focused")}),this.dropdown.on("mouseup",g,this.bind(function(b){a(b.target).closest(".select2-result-selectable").length>0&&(this.highlightUnderEvent(b),this.selectHighlighted(b))})),this.dropdown.on("click mouseup mousedown touchstart touchend focusin",function(a){a.stopPropagation()}),this.nextSearchTerm=b,a.isFunction(this.opts.initSelection)&&(this.initSelection(),this.monitorSource()),null!==c.maximumInputLength&&this.search.attr("maxlength",c.maximumInputLength);var h=c.element.prop("disabled");h===b&&(h=!1),this.enable(!h);var i=c.element.prop("readonly");i===b&&(i=!1),this.readonly(i),j=j||q(),this.autofocus=c.element.prop("autofocus"),c.element.prop("autofocus",!1),this.autofocus&&this.focus(),this.search.attr("placeholder",c.searchInputPlaceholder)},destroy:function(){var a=this.opts.element,c=a.data("select2"),d=this;this.close(),a.length&&a[0].detachEvent&&d._sync&&a.each(function(){d._sync&&this.detachEvent("onpropertychange",d._sync)}),this.propertyObserver&&(this.propertyObserver.disconnect(),this.propertyObserver=null),this._sync=null,c!==b&&(c.container.remove(),c.liveRegion.remove(),c.dropdown.remove(),a.show().removeData("select2").off(".select2").prop("autofocus",this.autofocus||!1),this.elementTabIndex?a.attr({tabindex:this.elementTabIndex}):a.removeAttr("tabindex"),a.show()),N.call(this,"container","liveRegion","dropdown","results","search")},optionToData:function(a){return a.is("option")?{id:a.prop("value"),text:a.text(),element:a.get(),css:a.attr("class"),disabled:a.prop("disabled"),locked:r(a.attr("locked"),"locked")||r(a.data("locked"),!0)}:a.is("optgroup")?{text:a.attr("label"),children:[],element:a.get(),css:a.attr("class")}:void 0},prepareOpts:function(c){var d,e,g,h,i=this;if(d=c.element,"select"===d.get(0).tagName.toLowerCase()&&(this.select=e=c.element),e&&a.each(["id","multiple","ajax","query","createSearchChoice","initSelection","data","tags"],function(){if(this in c)throw new Error("Option '"+this+"' is not allowed for Select2 when attached to a ","
"," ","
    ","
","
"].join(""));return b},enableInterface:function(){this.parent.enableInterface.apply(this,arguments)&&this.focusser.prop("disabled",!this.isInterfaceEnabled())},opening:function(){var c,d,e;this.opts.minimumResultsForSearch>=0&&this.showSearch(!0),this.parent.opening.apply(this,arguments),this.showSearchInput!==!1&&this.search.val(this.focusser.val()),this.opts.shouldFocusInput(this)&&(this.search.focus(),c=this.search.get(0),c.createTextRange?(d=c.createTextRange(),d.collapse(!1),d.select()):c.setSelectionRange&&(e=this.search.val().length,c.setSelectionRange(e,e))),""===this.search.val()&&this.nextSearchTerm!=b&&(this.search.val(this.nextSearchTerm),this.search.select()),this.focusser.prop("disabled",!0).val(""),this.updateResults(!0),this.opts.element.trigger(a.Event("select2-open"))},close:function(){this.opened()&&(this.parent.close.apply(this,arguments),this.focusser.prop("disabled",!1),this.opts.shouldFocusInput(this)&&this.focusser.focus())},focus:function(){this.opened()?this.close():(this.focusser.prop("disabled",!1),this.opts.shouldFocusInput(this)&&this.focusser.focus())},isFocused:function(){return this.container.hasClass("select2-container-active")},cancel:function(){this.parent.cancel.apply(this,arguments),this.focusser.prop("disabled",!1),this.opts.shouldFocusInput(this)&&this.focusser.focus()},destroy:function(){a("label[for='"+this.focusser.attr("id")+"']").attr("for",this.opts.element.attr("id")),this.parent.destroy.apply(this,arguments),N.call(this,"selection","focusser")},initContainer:function(){var b,g,c=this.container,d=this.dropdown,e=f();this.opts.minimumResultsForSearch<0?this.showSearch(!1):this.showSearch(!0),this.selection=b=c.find(".select2-choice"),this.focusser=c.find(".select2-focusser"),b.find(".select2-chosen").attr("id","select2-chosen-"+e),this.focusser.attr("aria-labelledby","select2-chosen-"+e),this.results.attr("id","select2-results-"+e),this.search.attr("aria-owns","select2-results-"+e),this.focusser.attr("id","s2id_autogen"+e),g=a("label[for='"+this.opts.element.attr("id")+"']"),this.opts.element.focus(this.bind(function(){this.focus()})),this.focusser.prev().text(g.text()).attr("for",this.focusser.attr("id"));var h=this.opts.element.attr("title");this.opts.element.attr("title",h||g.text()),this.focusser.attr("tabindex",this.elementTabIndex),this.search.attr("id",this.focusser.attr("id")+"_search"),this.search.prev().text(a("label[for='"+this.focusser.attr("id")+"']").text()).attr("for",this.search.attr("id")),this.search.on("keydown",this.bind(function(a){if(this.isInterfaceEnabled()&&229!=a.keyCode){if(a.which===k.PAGE_UP||a.which===k.PAGE_DOWN)return A(a),void 0;switch(a.which){case k.UP:case k.DOWN:return this.moveHighlight(a.which===k.UP?-1:1),A(a),void 0;case k.ENTER:return this.selectHighlighted(),A(a),void 0;case k.TAB:return this.selectHighlighted({noFocus:!0}),void 0;case k.ESC:return this.cancel(a),A(a),void 0}}})),this.search.on("blur",this.bind(function(){document.activeElement===this.body.get(0)&&window.setTimeout(this.bind(function(){this.opened()&&this.search.focus()}),0)})),this.focusser.on("keydown",this.bind(function(a){if(this.isInterfaceEnabled()&&a.which!==k.TAB&&!k.isControl(a)&&!k.isFunctionKey(a)&&a.which!==k.ESC){if(this.opts.openOnEnter===!1&&a.which===k.ENTER)return A(a),void 0;if(a.which==k.DOWN||a.which==k.UP||a.which==k.ENTER&&this.opts.openOnEnter){if(a.altKey||a.ctrlKey||a.shiftKey||a.metaKey)return;return this.open(),A(a),void 0}return a.which==k.DELETE||a.which==k.BACKSPACE?(this.opts.allowClear&&this.clear(),A(a),void 0):void 0}})),u(this.focusser),this.focusser.on("keyup-change input",this.bind(function(a){if(this.opts.minimumResultsForSearch>=0){if(a.stopPropagation(),this.opened())return;this.open()}})),b.on("mousedown touchstart","abbr",this.bind(function(a){this.isInterfaceEnabled()&&(this.clear(),B(a),this.close(),this.selection&&this.selection.focus())})),b.on("mousedown touchstart",this.bind(function(c){n(b),this.container.hasClass("select2-container-active")||this.opts.element.trigger(a.Event("select2-focus")),this.opened()?this.close():this.isInterfaceEnabled()&&this.open(),A(c)})),d.on("mousedown touchstart",this.bind(function(){this.opts.shouldFocusInput(this)&&this.search.focus()})),b.on("focus",this.bind(function(a){A(a)})),this.focusser.on("focus",this.bind(function(){this.container.hasClass("select2-container-active")||this.opts.element.trigger(a.Event("select2-focus")),this.container.addClass("select2-container-active")})).on("blur",this.bind(function(){this.opened()||(this.container.removeClass("select2-container-active"),this.opts.element.trigger(a.Event("select2-blur")))})),this.search.on("focus",this.bind(function(){this.container.hasClass("select2-container-active")||this.opts.element.trigger(a.Event("select2-focus")),this.container.addClass("select2-container-active")})),this.initContainerWidth(),this.opts.element.hide(),this.setPlaceholder()},clear:function(b){var c=this.selection.data("select2-data");if(c){var d=a.Event("select2-clearing");if(this.opts.element.trigger(d),d.isDefaultPrevented())return;var e=this.getPlaceholderOption();this.opts.element.val(e?e.val():""),this.selection.find(".select2-chosen").empty(),this.selection.removeData("select2-data"),this.setPlaceholder(),b!==!1&&(this.opts.element.trigger({type:"select2-removed",val:this.id(c),choice:c}),this.triggerChange({removed:c}))}},initSelection:function(){if(this.isPlaceholderOptionSelected())this.updateSelection(null),this.close(),this.setPlaceholder();else{var c=this;this.opts.initSelection.call(null,this.opts.element,function(a){a!==b&&null!==a&&(c.updateSelection(a),c.close(),c.setPlaceholder(),c.nextSearchTerm=c.opts.nextSearchTerm(a,c.search.val()))})}},isPlaceholderOptionSelected:function(){var a;return this.getPlaceholder()===b?!1:(a=this.getPlaceholderOption())!==b&&a.prop("selected")||""===this.opts.element.val()||this.opts.element.val()===b||null===this.opts.element.val()},prepareOpts:function(){var b=this.parent.prepareOpts.apply(this,arguments),c=this;return"select"===b.element.get(0).tagName.toLowerCase()?b.initSelection=function(a,b){var d=a.find("option").filter(function(){return this.selected&&!this.disabled});b(c.optionToData(d))}:"data"in b&&(b.initSelection=b.initSelection||function(c,d){var e=c.val(),f=null;b.query({matcher:function(a,c,d){var g=r(e,b.id(d));return g&&(f=d),g},callback:a.isFunction(d)?function(){d(f)}:a.noop})}),b},getPlaceholder:function(){return this.select&&this.getPlaceholderOption()===b?b:this.parent.getPlaceholder.apply(this,arguments)},setPlaceholder:function(){var a=this.getPlaceholder();if(this.isPlaceholderOptionSelected()&&a!==b){if(this.select&&this.getPlaceholderOption()===b)return;this.selection.find(".select2-chosen").html(this.opts.escapeMarkup(a)),this.selection.addClass("select2-default"),this.container.removeClass("select2-allowclear")}},postprocessResults:function(a,b,c){var d=0,e=this;if(this.findHighlightableChoices().each2(function(a,b){return r(e.id(b.data("select2-data")),e.opts.element.val())?(d=a,!1):void 0}),c!==!1&&(b===!0&&d>=0?this.highlight(d):this.highlight(0)),b===!0){var g=this.opts.minimumResultsForSearch;g>=0&&this.showSearch(L(a.results)>=g)}},showSearch:function(b){this.showSearchInput!==b&&(this.showSearchInput=b,this.dropdown.find(".select2-search").toggleClass("select2-search-hidden",!b),this.dropdown.find(".select2-search").toggleClass("select2-offscreen",!b),a(this.dropdown,this.container).toggleClass("select2-with-searchbox",b))},onSelect:function(a,b){if(this.triggerSelect(a)){var c=this.opts.element.val(),d=this.data();this.opts.element.val(this.id(a)),this.updateSelection(a),this.opts.element.trigger({type:"select2-selected",val:this.id(a),choice:a}),this.nextSearchTerm=this.opts.nextSearchTerm(a,this.search.val()),this.close(),b&&b.noFocus||!this.opts.shouldFocusInput(this)||this.focusser.focus(),r(c,this.id(a))||this.triggerChange({added:a,removed:d})}},updateSelection:function(a){var d,e,c=this.selection.find(".select2-chosen");this.selection.data("select2-data",a),c.empty(),null!==a&&(d=this.opts.formatSelection(a,c,this.opts.escapeMarkup)),d!==b&&c.append(d),e=this.opts.formatSelectionCssClass(a,c),e!==b&&c.addClass(e),this.selection.removeClass("select2-default"),this.opts.allowClear&&this.getPlaceholder()!==b&&this.container.addClass("select2-allowclear")},val:function(){var a,c=!1,d=null,e=this,f=this.data();if(0===arguments.length)return this.opts.element.val();if(a=arguments[0],arguments.length>1&&(c=arguments[1]),this.select)this.select.val(a).find("option").filter(function(){return this.selected}).each2(function(a,b){return d=e.optionToData(b),!1}),this.updateSelection(d),this.setPlaceholder(),c&&this.triggerChange({added:d,removed:f});else{if(!a&&0!==a)return this.clear(c),void 0;if(this.opts.initSelection===b)throw new Error("cannot call val() if initSelection() is not defined");this.opts.element.val(a),this.opts.initSelection(this.opts.element,function(a){e.opts.element.val(a?e.id(a):""),e.updateSelection(a),e.setPlaceholder(),c&&e.triggerChange({added:a,removed:f})})}},clearSearch:function(){this.search.val(""),this.focusser.val("")},data:function(a){var c,d=!1;return 0===arguments.length?(c=this.selection.data("select2-data"),c==b&&(c=null),c):(arguments.length>1&&(d=arguments[1]),a?(c=this.data(),this.opts.element.val(a?this.id(a):""),this.updateSelection(a),d&&this.triggerChange({added:a,removed:c})):this.clear(d),void 0)}}),e=O(c,{createContainer:function(){var b=a(document.createElement("div")).attr({"class":"select2-container select2-container-multi"}).html(["
    ","
  • "," "," ","
  • ","
","
","
    ","
","
"].join(""));return b},prepareOpts:function(){var b=this.parent.prepareOpts.apply(this,arguments),c=this;return"select"===b.element.get(0).tagName.toLowerCase()?b.initSelection=function(a,b){var d=[];a.find("option").filter(function(){return this.selected&&!this.disabled}).each2(function(a,b){d.push(c.optionToData(b))}),b(d)}:"data"in b&&(b.initSelection=b.initSelection||function(c,d){var e=s(c.val(),b.separator,b.transformVal),f=[];b.query({matcher:function(c,d,g){var h=a.grep(e,function(a){return r(a,b.id(g))}).length;return h&&f.push(g),h},callback:a.isFunction(d)?function(){for(var a=[],c=0;c0||(this.selectChoice(null),this.clearPlaceholder(),this.container.hasClass("select2-container-active")||this.opts.element.trigger(a.Event("select2-focus")),this.open(),this.focusSearch(),b.preventDefault()))})),this.container.on("focus",b,this.bind(function(){this.isInterfaceEnabled()&&(this.container.hasClass("select2-container-active")||this.opts.element.trigger(a.Event("select2-focus")),this.container.addClass("select2-container-active"),this.dropdown.addClass("select2-drop-active"),this.clearPlaceholder())})),this.initContainerWidth(),this.opts.element.hide(),this.clearSearch()},enableInterface:function(){this.parent.enableInterface.apply(this,arguments)&&this.search.prop("disabled",!this.isInterfaceEnabled())},initSelection:function(){if(""===this.opts.element.val()&&""===this.opts.element.text()&&(this.updateSelection([]),this.close(),this.clearSearch()),this.select||""!==this.opts.element.val()){var c=this;this.opts.initSelection.call(null,this.opts.element,function(a){a!==b&&null!==a&&(c.updateSelection(a),c.close(),c.clearSearch())})}},clearSearch:function(){var a=this.getPlaceholder(),c=this.getMaxSearchWidth();a!==b&&0===this.getVal().length&&this.search.hasClass("select2-focused")===!1?(this.search.val(a).addClass("select2-default"),this.search.width(c>0?c:this.container.css("width"))):this.search.val("").width(10)},clearPlaceholder:function(){this.search.hasClass("select2-default")&&this.search.val("").removeClass("select2-default")},opening:function(){this.clearPlaceholder(),this.resizeSearch(),this.parent.opening.apply(this,arguments),this.focusSearch(),""===this.search.val()&&this.nextSearchTerm!=b&&(this.search.val(this.nextSearchTerm),this.search.select()),this.updateResults(!0),this.opts.shouldFocusInput(this)&&this.search.focus(),this.opts.element.trigger(a.Event("select2-open"))},close:function(){this.opened()&&this.parent.close.apply(this,arguments)},focus:function(){this.close(),this.search.focus()},isFocused:function(){return this.search.hasClass("select2-focused")},updateSelection:function(b){var c=[],d=[],e=this;a(b).each(function(){p(e.id(this),c)<0&&(c.push(e.id(this)),d.push(this))}),b=d,this.selection.find(".select2-search-choice").remove(),a(b).each(function(){e.addSelectedChoice(this)}),e.postprocessResults()},tokenize:function(){var a=this.search.val();a=this.opts.tokenizer.call(this,a,this.data(),this.bind(this.onSelect),this.opts),null!=a&&a!=b&&(this.search.val(a),a.length>0&&this.open())},onSelect:function(a,c){this.triggerSelect(a)&&""!==a.text&&(this.addSelectedChoice(a),this.opts.element.trigger({type:"selected",val:this.id(a),choice:a}),this.nextSearchTerm=this.opts.nextSearchTerm(a,this.search.val()),this.clearSearch(),this.updateResults(),(this.select||!this.opts.closeOnSelect)&&this.postprocessResults(a,!1,this.opts.closeOnSelect===!0),this.opts.closeOnSelect?(this.close(),this.search.width(10)):this.countSelectableResults()>0?(this.search.width(10),this.resizeSearch(),this.getMaximumSelectionSize()>0&&this.val().length>=this.getMaximumSelectionSize()?this.updateResults(!0):this.nextSearchTerm!=b&&(this.search.val(this.nextSearchTerm),this.updateResults(),this.search.select()),this.positionDropdown()):(this.close(),this.search.width(10)),this.triggerChange({added:a}),c&&c.noFocus||this.focusSearch())},cancel:function(){this.close(),this.focusSearch()},addSelectedChoice:function(c){var j,k,d=!c.locked,e=a("
  • "),f=a("
  • "),g=d?e:f,h=this.id(c),i=this.getVal();j=this.opts.formatSelection(c,g.find("div"),this.opts.escapeMarkup),j!=b&&g.find("div").replaceWith(a("
    ").html(j)),k=this.opts.formatSelectionCssClass(c,g.find("div")),k!=b&&g.addClass(k),d&&g.find(".select2-search-choice-close").on("mousedown",A).on("click dblclick",this.bind(function(b){this.isInterfaceEnabled()&&(this.unselect(a(b.target)),this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus"),A(b),this.close(),this.focusSearch())})).on("focus",this.bind(function(){this.isInterfaceEnabled()&&(this.container.addClass("select2-container-active"),this.dropdown.addClass("select2-drop-active"))})),g.data("select2-data",c),g.insertBefore(this.searchContainer),i.push(h),this.setVal(i)},unselect:function(b){var d,e,c=this.getVal();if(b=b.closest(".select2-search-choice"),0===b.length)throw"Invalid argument: "+b+". Must be .select2-search-choice";if(d=b.data("select2-data")){var f=a.Event("select2-removing");if(f.val=this.id(d),f.choice=d,this.opts.element.trigger(f),f.isDefaultPrevented())return!1;for(;(e=p(this.id(d),c))>=0;)c.splice(e,1),this.setVal(c),this.select&&this.postprocessResults();return b.remove(),this.opts.element.trigger({type:"select2-removed",val:this.id(d),choice:d}),this.triggerChange({removed:d}),!0}},postprocessResults:function(a,b,c){var d=this.getVal(),e=this.results.find(".select2-result"),f=this.results.find(".select2-result-with-children"),g=this;e.each2(function(a,b){var c=g.id(b.data("select2-data"));p(c,d)>=0&&(b.addClass("select2-selected"),b.find(".select2-result-selectable").addClass("select2-selected"))}),f.each2(function(a,b){b.is(".select2-result-selectable")||0!==b.find(".select2-result-selectable:not(.select2-selected)").length||b.addClass("select2-selected")}),-1==this.highlight()&&c!==!1&&this.opts.closeOnSelect===!0&&g.highlight(0),!this.opts.createSearchChoice&&!e.filter(".select2-result:not(.select2-selected)").length>0&&(!a||a&&!a.more&&0===this.results.find(".select2-no-results").length)&&J(g.opts.formatNoMatches,"formatNoMatches")&&this.results.append("
  • "+K(g.opts.formatNoMatches,g.opts.element,g.search.val())+"
  • ")},getMaxSearchWidth:function(){return this.selection.width()-t(this.search)},resizeSearch:function(){var a,b,c,d,e,f=t(this.search);a=C(this.search)+10,b=this.search.offset().left,c=this.selection.width(),d=this.selection.offset().left,e=c-(b-d)-f,a>e&&(e=c-f),40>e&&(e=c-f),0>=e&&(e=a),this.search.width(Math.floor(e))},getVal:function(){var a;return this.select?(a=this.select.val(),null===a?[]:a):(a=this.opts.element.val(),s(a,this.opts.separator,this.opts.transformVal))},setVal:function(b){var c;this.select?this.select.val(b):(c=[],a(b).each(function(){p(this,c)<0&&c.push(this)}),this.opts.element.val(0===c.length?"":c.join(this.opts.separator)))},buildChangeDetails:function(a,b){for(var b=b.slice(0),a=a.slice(0),c=0;c0&&c--,a.splice(d,1),d--);return{added:b,removed:a}},val:function(c,d){var e,f=this;if(0===arguments.length)return this.getVal();if(e=this.data(),e.length||(e=[]),!c&&0!==c)return this.opts.element.val(""),this.updateSelection([]),this.clearSearch(),d&&this.triggerChange({added:this.data(),removed:e}),void 0;if(this.setVal(c),this.select)this.opts.initSelection(this.select,this.bind(this.updateSelection)),d&&this.triggerChange(this.buildChangeDetails(e,this.data()));else{if(this.opts.initSelection===b)throw new Error("val() cannot be called if initSelection() is not defined");this.opts.initSelection(this.opts.element,function(b){var c=a.map(b,f.id);f.setVal(c),f.updateSelection(b),f.clearSearch(),d&&f.triggerChange(f.buildChangeDetails(e,f.data()))})}this.clearSearch()},onSortStart:function(){if(this.select)throw new Error("Sorting of elements is not supported when attached to instead.");this.search.width(0),this.searchContainer.hide()},onSortEnd:function(){var b=[],c=this;this.searchContainer.show(),this.searchContainer.appendTo(this.searchContainer.parent()),this.resizeSearch(),this.selection.find(".select2-search-choice").each(function(){b.push(c.opts.id(a(this).data("select2-data")))}),this.setVal(b),this.triggerChange()},data:function(b,c){var e,f,d=this;return 0===arguments.length?this.selection.children(".select2-search-choice").map(function(){return a(this).data("select2-data")}).get():(f=this.data(),b||(b=[]),e=a.map(b,function(a){return d.opts.id(a)}),this.setVal(e),this.updateSelection(b),this.clearSearch(),c&&this.triggerChange(this.buildChangeDetails(f,this.data())),void 0)}}),a.fn.select2=function(){var d,e,f,g,h,c=Array.prototype.slice.call(arguments,0),i=["val","destroy","opened","open","close","focus","isFocused","container","dropdown","onSortStart","onSortEnd","enable","disable","readonly","positionDropdown","data","search"],j=["opened","isFocused","container","dropdown"],k=["val","data"],l={search:"externalSearch"};return this.each(function(){if(0===c.length||"object"==typeof c[0])d=0===c.length?{}:a.extend({},c[0]),d.element=a(this),"select"===d.element.get(0).tagName.toLowerCase()?h=d.element.prop("multiple"):(h=d.multiple||!1,"tags"in d&&(d.multiple=h=!0)),e=h?new window.Select2["class"].multi:new window.Select2["class"].single,e.init(d);else{if("string"!=typeof c[0])throw"Invalid arguments to select2 plugin: "+c;if(p(c[0],i)<0)throw"Unknown method: "+c[0];if(g=b,e=a(this).data("select2"),e===b)return;if(f=c[0],"container"===f?g=e.container:"dropdown"===f?g=e.dropdown:(l[f]&&(f=l[f]),g=e[f].apply(e,c.slice(1))),p(c[0],j)>=0||p(c[0],k)>=0&&1==c.length)return!1}}),g===b?this:g},a.fn.select2.defaults={width:"copy",loadMorePadding:0,closeOnSelect:!0,openOnEnter:!0,containerCss:{},dropdownCss:{},containerCssClass:"",dropdownCssClass:"",formatResult:function(a,b,c,d){var e=[];return E(this.text(a),c.term,e,d),e.join("")},transformVal:function(b){return a.trim(b)},formatSelection:function(a,c,d){return a?d(this.text(a)):b},sortResults:function(a){return a},formatResultCssClass:function(a){return a.css},formatSelectionCssClass:function(){return b},minimumResultsForSearch:0,minimumInputLength:0,maximumInputLength:null,maximumSelectionSize:0,id:function(a){return a==b?null:a.id},text:function(b){return b&&this.data&&this.data.text?a.isFunction(this.data.text)?this.data.text(b):b[this.data.text]:b.text +},matcher:function(a,b){return o(""+b).toUpperCase().indexOf(o(""+a).toUpperCase())>=0},separator:",",tokenSeparators:[],tokenizer:M,escapeMarkup:F,blurOnChange:!1,selectOnBlur:!1,adaptContainerCssClass:function(a){return a},adaptDropdownCssClass:function(){return null},nextSearchTerm:function(){return b},searchInputPlaceholder:"",createSearchChoicePosition:"top",shouldFocusInput:function(a){var b="ontouchstart"in window||navigator.msMaxTouchPoints>0;return b?a.opts.minimumResultsForSearch<0?!1:!0:!0}},a.fn.select2.locales=[],a.fn.select2.locales.en={formatMatches:function(a){return 1===a?"One result is available, press enter to select it.":a+" results are available, use up and down arrow keys to navigate."},formatNoMatches:function(){return"No matches found"},formatAjaxError:function(){return"Loading failed"},formatInputTooShort:function(a,b){var c=b-a.length;return"Please enter "+c+" or more character"+(1==c?"":"s")},formatInputTooLong:function(a,b){var c=a.length-b;return"Please delete "+c+" character"+(1==c?"":"s")},formatSelectionTooBig:function(a){return"You can only select "+a+" item"+(1==a?"":"s")},formatLoadMore:function(){return"Loading more results\u2026"},formatSearching:function(){return"Searching\u2026"}},a.extend(a.fn.select2.defaults,a.fn.select2.locales.en),a.fn.select2.ajaxDefaults={transport:a.ajax,params:{type:"GET",cache:!1,dataType:"json"}},window.Select2={query:{ajax:G,local:H,tags:I},util:{debounce:w,markMatch:E,escapeMarkup:F,stripDiacritics:o},"class":{"abstract":c,single:d,multi:e}}}}(jQuery); \ No newline at end of file