- {{tag-chooser tags=searchedTerms.tags blacklist=searchedTerms.tags allowCreate=false placeholder="" everyTag="true" unlimitedTagCount="true" width="70%"}}
+ {{tag-chooser
+ tags=searchedTerms.tags
+ allowCreate=false
+ filterPlaceholder=null
+ everyTag=true
+ unlimitedTagCount=true}}
{{ input type="checkbox" class="all-tags" checked=searchedTerms.special.all_tags}} {{i18n "search.advanced.filters.all_tags"}}
diff --git a/app/assets/javascripts/discourse/templates/preferences/tags.hbs b/app/assets/javascripts/discourse/templates/preferences/tags.hbs
index 1c24147a2f6..af170058de8 100644
--- a/app/assets/javascripts/discourse/templates/preferences/tags.hbs
+++ b/app/assets/javascripts/discourse/templates/preferences/tags.hbs
@@ -5,25 +5,49 @@
{{d-icon "d-watching" class="icon watching"}} {{i18n 'user.watched_tags'}}
- {{tag-chooser tags=model.watched_tags blacklist=selectedTags allowCreate=false placeholder="" everyTag="true" unlimitedTagCount="true"}}
+ {{tag-chooser
+ tags=model.watched_tags
+ blacklist=selectedTags
+ filterPlaceholder=null
+ allowCreate=false
+ everyTag=true
+ unlimitedTagCount=true}}
{{i18n 'user.watched_tags_instructions'}}
{{d-icon "d-regular" class="icon tracking"}} {{i18n 'user.tracked_tags'}}
- {{tag-chooser tags=model.tracked_tags blacklist=selectedTags allowCreate=false placeholder="" everyTag="true" unlimitedTagCount="true"}}
+ {{tag-chooser
+ tags=model.tracked_tags
+ blacklist=selectedTags
+ filterPlaceholder=null
+ allowCreate=false
+ everyTag=true
+ unlimitedTagCount=true}}
{{i18n 'user.tracked_tags_instructions'}}
{{d-icon "d-watching-first" class="icon watching-first-post"}} {{i18n 'user.watched_first_post_tags'}}
- {{tag-chooser tags=model.watching_first_post_tags blacklist=selectedTags allowCreate=false placeholder="" everyTag="true" unlimitedTagCount="true"}}
+ {{tag-chooser
+ tags=model.watching_first_post_tags
+ blacklist=selectedTags
+ filterPlaceholder=null
+ allowCreate=false
+ everyTag=true
+ unlimitedTagCount=true}}
{{i18n 'user.watched_first_post_tags_instructions'}}
{{d-icon "d-muted" class="icon muted"}} {{i18n 'user.muted_tags'}}
- {{tag-chooser tags=model.muted_tags blacklist=selectedTags allowCreate=false placeholder="" everyTag="true" unlimitedTagCount="true"}}
+ {{tag-chooser
+ tags=model.muted_tags
+ blacklist=selectedTags
+ filterPlaceholder=null
+ allowCreate=false
+ everyTag=true
+ unlimitedTagCount=true}}
{{i18n 'user.muted_tags_instructions'}}
diff --git a/app/assets/javascripts/discourse/templates/tag-groups-show.hbs b/app/assets/javascripts/discourse/templates/tag-groups-show.hbs
index 26fde8830f4..dec3a0e3e9a 100644
--- a/app/assets/javascripts/discourse/templates/tag-groups-show.hbs
+++ b/app/assets/javascripts/discourse/templates/tag-groups-show.hbs
@@ -3,12 +3,19 @@
{{i18n 'tagging.groups.parent_tag_label'}}
- {{tag-chooser tags=model.parent_tag_name everyTag="true" limit="1" placeholderKey="tagging.groups.parent_tag_placeholder"}}
+ {{tag-chooser
+ tags=model.parent_tag_name
+ everyTag=true
+ limit=1
+ filterPlaceholder="tagging.groups.parent_tag_placeholder"}}
{{i18n 'tagging.groups.parent_tag_description'}}
diff --git a/app/assets/javascripts/discourse/templates/topic.hbs b/app/assets/javascripts/discourse/templates/topic.hbs
index 167ffcf138b..810679b3741 100644
--- a/app/assets/javascripts/discourse/templates/topic.hbs
+++ b/app/assets/javascripts/discourse/templates/topic.hbs
@@ -22,7 +22,7 @@
{{/if}}
{{#if canEditTags}}
- {{tag-chooser tags=buffered.tags categoryId=buffered.category_id}}
+ {{mini-tag-chooser tags=buffered.tags categoryId=buffered.category_id}}
{{/if}}
{{plugin-outlet name="edit-topic" args=(hash model=model buffered=buffered)}}
diff --git a/app/assets/javascripts/select-kit/components/mini-tag-chooser.js.es6 b/app/assets/javascripts/select-kit/components/mini-tag-chooser.js.es6
index d28f7a1caae..1a9d4028901 100644
--- a/app/assets/javascripts/select-kit/components/mini-tag-chooser.js.es6
+++ b/app/assets/javascripts/select-kit/components/mini-tag-chooser.js.es6
@@ -1,22 +1,22 @@
import ComboBox from "select-kit/components/combo-box";
-import { ajax } from "discourse/lib/ajax";
-import { popupAjaxError } from 'discourse/lib/ajax-error';
+import Tags from "select-kit/mixins/tags";
import { default as computed } from "ember-addons/ember-computed-decorators";
import renderTag from "discourse/lib/render-tag";
-const { get, isEmpty, isPresent, run, makeArray } = Ember;
+const { get, isEmpty, run, makeArray } = Ember;
-export default ComboBox.extend({
+export default ComboBox.extend(Tags, {
allowContentReplacement: true,
pluginApiIdentifiers: ["mini-tag-chooser"],
+ attributeBindings: ["categoryId"],
classNames: ["mini-tag-chooser"],
classNameBindings: ["noTags"],
verticalOffset: 3,
filterable: true,
- noTags: Ember.computed.empty("computedTags"),
+ noTags: Ember.computed.empty("selectedTags"),
allowAny: true,
- maximumSelectionSize: Ember.computed.alias("siteSettings.max_tags_per_topic"),
caretUpIcon: Ember.computed.alias("caretIcon"),
caretDownIcon: Ember.computed.alias("caretIcon"),
+ isAsync: true,
init() {
this._super();
@@ -30,17 +30,8 @@ export default ComboBox.extend({
noHref: true
});
});
- },
- @computed("limitReached", "maximumSelectionSize")
- maxContentRow(limitReached, count) {
- if (limitReached) {
- return I18n.t("select_kit.max_content_reached", { count });
- }
- },
-
- mutateAttributes() {
- this.set("value", null);
+ this.set("limit", parseInt(this.get("limit") || this.get("siteSettings.max_tags_per_topic")));
},
@computed("limitReached")
@@ -48,9 +39,9 @@ export default ComboBox.extend({
return limitReached ? null : "plus";
},
- @computed("computedTags.[]", "maximumSelectionSize")
- limitReached(computedTags, maximumSelectionSize) {
- if (computedTags.length >= maximumSelectionSize) {
+ @computed("selectedTags.[]", "limit")
+ limitReached(selectedTags, limit) {
+ if (selectedTags.length >= limit) {
return true;
}
@@ -58,33 +49,10 @@ export default ComboBox.extend({
},
@computed("tags")
- computedTags(tags) {
+ selectedTags(tags) {
return makeArray(tags);
},
- validateCreate(term) {
- if (this.get("limitReached") || !this.site.get("can_create_tag")) {
- return false;
- }
-
- const filterRegexp = new RegExp(this.site.tags_filter_regexp, "g");
- term = term.replace(filterRegexp, "").trim().toLowerCase();
-
- if (!term.length || this.get("termMatchesForbidden")) {
- return false;
- }
-
- if (this.get("siteSettings.max_tag_length") < term.length) {
- return false;
- }
-
- return true;
- },
-
- validateSelect() {
- return this.get("computedTags").length < this.get("siteSettings.max_tags_per_topic");
- },
-
filterComputedContent(computedContent) {
return computedContent;
},
@@ -92,7 +60,7 @@ export default ComboBox.extend({
didRender() {
this._super();
- this.$().on("click.mini-tag-chooser", ".selected-tag", (event) => {
+ $(".select-kit-body").on("click.mini-tag-chooser", ".selected-tag", (event) => {
event.stopImmediatePropagation();
this.send("removeTag", $(event.target).attr("data-value"));
});
@@ -102,9 +70,6 @@ export default ComboBox.extend({
this._super();
$(".select-kit-body").off("click.mini-tag-chooser");
-
- const searchDebounce = this.get("searchDebounce");
- if (isPresent(searchDebounce)) { run.cancel(searchDebounce); }
},
didPressEscape(event) {
@@ -167,9 +132,9 @@ export default ComboBox.extend({
computeHeaderContent() {
let content = this.baseHeaderComputedContent();
- const joinedTags = this.get("computedTags").join(", ");
+ const joinedTags = this.get("selectedTags").join(", ");
- if (isEmpty(this.get("computedTags"))) {
+ if (isEmpty(this.get("selectedTags"))) {
content.label = I18n.t("tagging.choose_for_topic");
} else {
content.label = joinedTags;
@@ -182,80 +147,61 @@ export default ComboBox.extend({
actions: {
removeTag(tag) {
- let tags = this.get("computedTags");
+ let tags = this.get("selectedTags");
delete tags[tags.indexOf(tag)];
this.set("tags", tags.filter(t => t));
- this.set("content", []);
- this.set("searchDebounce", run.debounce(this, this._searchTags, this.get("filter"), 250));
+ this.set("searchDebounce", run.debounce(this, this.prepareSearch, this.get("filter"), 200));
},
onExpand() {
- if (isEmpty(this.get("content"))) {
- this.set("searchDebounce", run.debounce(this, this._searchTags, this.get("filter"), 250));
+ if (isEmpty(this.get("collectionComputedContent"))) {
+ this.set("searchDebounce", run.debounce(this, this.prepareSearch, this.get("filter"), 200));
}
},
onFilter(filter) {
filter = isEmpty(filter) ? null : filter;
- this.set("searchDebounce", run.debounce(this, this._searchTags, filter, 250));
+ this.set("searchDebounce", run.debounce(this, this.prepareSearch, filter, 200));
},
onSelect(tag) {
- if (isEmpty(this.get("computedTags"))) {
+ if (isEmpty(this.get("selectedTags"))) {
this.set("tags", makeArray(tag));
} else {
- this.set("tags", this.get("computedTags").concat(tag));
+ this.set("tags", this.get("selectedTags").concat(tag));
}
- this.set("content", []);
- this.set("searchDebounce", run.debounce(this, this._searchTags, this.get("filter"), 250));
+ this.set("searchDebounce", run.debounce(this, this.prepareSearch, this.get("filter"), 50));
+
+ this.autoHighlight();
}
},
- _searchTags(query) {
- this.startLoading();
-
- const self = this;
- const selectedTags = makeArray(this.get("computedTags")).filter(t => t);
- const sortTags = this.siteSettings.tags_sort_alphabetically;
+ prepareSearch(query) {
const data = {
q: query,
- limit: this.siteSettings.max_tag_search_results,
+ limit: this.get("siteSettings.max_tag_search_results"),
categoryId: this.get("categoryId")
};
+ if (this.get("selectedTags")) data.selected_tags = this.get("selectedTags").slice(0, 100);
+ if (!this.get("everyTag")) data.filterForInput = true;
- if (selectedTags) {
- data.selected_tags = selectedTags.slice(0, 100);
+ this.searchTags("/tags/filter/search", data, this._transformJson);
+ },
+
+ _transformJson(context, json) {
+ let results = json.results;
+
+ context.set("termMatchesForbidden", json.forbidden ? true : false);
+
+ if (context.get("siteSettings.tags_sort_alphabetically")) {
+ results = results.sort((a, b) => a.id > b.id);
}
- ajax(Discourse.getURL("/tags/filter/search"), {
- quietMillis: 200,
- cache: true,
- dataType: "json",
- data,
- }).then(json => {
- let results = json.results;
+ results = results.filter(r => !context.get("selectedTags").includes(r.id));
- self.set("termMatchesForbidden", json.forbidden ? true : false);
-
- if (sortTags) {
- results = results.sort((a, b) => a.id > b.id);
- }
-
- const content = results.map((result) => {
- return {
- id: result.text,
- name: result.text,
- count: result.count
- };
- }).filter(c => !selectedTags.includes(c.id));
-
- self.set("content", content);
- self.stopLoading();
- this.autoHighlight();
- }).catch(error => {
- self.stopLoading();
- popupAjaxError(error);
- });
+ return results.map(result => {
+ return { id: result.text, name: result.text, count: result.count };
+ });
}
});
diff --git a/app/assets/javascripts/select-kit/components/multi-select.js.es6 b/app/assets/javascripts/select-kit/components/multi-select.js.es6
index e3e0ad9a132..b74b371c59c 100644
--- a/app/assets/javascripts/select-kit/components/multi-select.js.es6
+++ b/app/assets/javascripts/select-kit/components/multi-select.js.es6
@@ -8,14 +8,15 @@ import {
export default SelectKitComponent.extend({
pluginApiIdentifiers: ["multi-select"],
+ layoutName: "select-kit/templates/components/multi-select",
classNames: "multi-select",
headerComponent: "multi-select/multi-select-header",
- filterComponent: null,
headerText: "select_kit.default_header_text",
allowAny: true,
allowInitialValueMutation: false,
autoFilterable: true,
selectedNameComponent: "multi-select/selected-name",
+ filterIcon: null,
init() {
this._super();
@@ -38,16 +39,22 @@ export default SelectKitComponent.extend({
_compute() {
Ember.run.scheduleOnce("afterRender", () => {
this.willComputeAttributes();
- let content = this.willComputeContent(this.get("content") || []);
+ let content = this.get("content") || [];
+ let asyncContent = this.get("asyncContent") || [];
+ content = this.willComputeContent(content);
+ asyncContent = this.willComputeAsyncContent(asyncContent);
let values = this._beforeWillComputeValues(this.get("values"));
content = this.computeContent(content);
+ asyncContent = this.computeAsyncContent(asyncContent);
content = this._beforeDidComputeContent(content);
+ asyncContent = this._beforeDidComputeAsyncContent(asyncContent);
values = this.willComputeValues(values);
values = this.computeValues(values);
values = this._beforeDidComputeValues(values);
this._setHeaderComputedContent();
this._setCollectionHeaderComputedContent();
this.didComputeContent(content);
+ this.didComputeAsyncContent(asyncContent);
this.didComputeValues(values);
this.didComputeAttributes();
});
@@ -102,6 +109,19 @@ export default SelectKitComponent.extend({
});
},
+ @computed("computedAsyncContent.[]", "computedValues.[]")
+ filteredAsyncComputedContent(computedAsyncContent, computedValues) {
+ computedAsyncContent = computedAsyncContent.filter(c => {
+ return !computedValues.includes(get(c, "value"));
+ });
+
+ if (this.get("limitMatches")) {
+ return computedAsyncContent.slice(0, this.get("limitMatches"));
+ }
+
+ return computedAsyncContent;
+ },
+
@computed("computedContent.[]", "computedValues.[]", "filter")
filteredComputedContent(computedContent, computedValues, filter) {
computedContent = computedContent.filter(c => {
@@ -133,6 +153,16 @@ export default SelectKitComponent.extend({
};
},
+ @computed("limit", "computedValues.[]")
+ limitReached(limit, computedValues) {
+ if (!limit) return false;
+ return computedValues.length >= limit;
+ },
+
+ validateSelect() {
+ return this._super() && !this.get("limitReached");
+ },
+
didPressBackspace(event) {
this.expand(event);
this.keyDown(event);
@@ -178,16 +208,16 @@ export default SelectKitComponent.extend({
if ($lastSelectedValue.length === 0) { return; }
if ($filterInput.not(":visible") && $lastSelectedValue.length > 0) {
- $lastSelectedValue.click();
+ $lastSelectedValue.trigger("backspace");
return false;
}
if ($filterInput.val() === "") {
if ($filterInput.is(":focus")) {
- if ($lastSelectedValue.length > 0) { $lastSelectedValue.click(); }
+ if ($lastSelectedValue.length > 0) { $lastSelectedValue.trigger("backspace"); }
} else {
if ($lastSelectedValue.length > 0) {
- $lastSelectedValue.click();
+ $lastSelectedValue.trigger("backspace");
} else {
$filterInput.focus();
}
@@ -217,14 +247,14 @@ export default SelectKitComponent.extend({
if (!this.get("renderedBodyOnce")) return;
if (!isNone(this.get("highlightedValue"))) return;
- if (isEmpty(this.get("filteredComputedContent"))) {
+ if (isEmpty(this.get("collectionComputedContent"))) {
if (this.get("createRowComputedContent")) {
this.send("highlight", this.get("createRowComputedContent"));
} else if (this.get("noneRowComputedContent") && this.get("hasSelection")) {
this.send("highlight", this.get("noneRowComputedContent"));
}
} else {
- this.send("highlight", this.get("filteredComputedContent.firstObject"));
+ this.send("highlight", this.get("collectionComputedContent.firstObject"));
}
});
},
@@ -247,9 +277,10 @@ export default SelectKitComponent.extend({
this.set("highlightedValue", null);
},
- didDeselect() {
+ didDeselect(rowComputedContentItems) {
this.focusFilterOrHeader();
this.autoHighlight();
+ this._boundaryActionHandler("onDeselect", rowComputedContentItems);
},
actions: {
diff --git a/app/assets/javascripts/select-kit/components/multi-select/selected-name.js.es6 b/app/assets/javascripts/select-kit/components/multi-select/selected-name.js.es6
index a6b672d50ed..eff2b9716d4 100644
--- a/app/assets/javascripts/select-kit/components/multi-select/selected-name.js.es6
+++ b/app/assets/javascripts/select-kit/components/multi-select/selected-name.js.es6
@@ -28,6 +28,20 @@ export default Ember.Component.extend({
return null;
},
+ didInsertElement() {
+ this._super();
+
+ $(this.element).on("backspace.selected-name", () => {
+ this._handleBackspace();
+ });
+ },
+
+ willDestroyElement() {
+ this._super();
+
+ $(this.element).off("backspace.selected-name");
+ },
+
label: Ember.computed.or("computedContent.label", "title", "name"),
name: Ember.computed.alias("computedContent.name"),
@@ -39,8 +53,18 @@ export default Ember.Component.extend({
}),
click() {
- if (this.get("isLocked") === true) { return false; }
- this.toggleProperty("isHighlighted");
+ if (this.get("isLocked") === true) return false;
+ this.sendAction("deselect", [this.get("computedContent")]);
return false;
+ },
+
+ _handleBackspace() {
+ if (this.get("isLocked") === true) return false;
+
+ if (this.get("isHighlighted")) {
+ this.sendAction("deselect", [this.get("computedContent")]);
+ } else {
+ this.set("isHighlighted", true);
+ }
}
});
diff --git a/app/assets/javascripts/select-kit/components/select-kit.js.es6 b/app/assets/javascripts/select-kit/components/select-kit.js.es6
index b8bdf5c6674..b8418805085 100644
--- a/app/assets/javascripts/select-kit/components/select-kit.js.es6
+++ b/app/assets/javascripts/select-kit/components/select-kit.js.es6
@@ -32,6 +32,7 @@ export default Ember.Component.extend(UtilsMixin, PluginApiMixin, DomHelpersMixi
isFocused: false,
isHidden: false,
isLoading: false,
+ isAsync: false,
renderedBodyOnce: false,
renderedFilterOnce: false,
tabindex: 0,
@@ -69,8 +70,6 @@ export default Ember.Component.extend(UtilsMixin, PluginApiMixin, DomHelpersMixi
allowContentReplacement: false,
collectionHeader: null,
allowAutoSelectFirst: true,
- maximumSelectionSize: null,
- maxContentRow: null,
init() {
this._super();
@@ -91,11 +90,16 @@ export default Ember.Component.extend(UtilsMixin, PluginApiMixin, DomHelpersMixi
if (this.get("allowContentReplacement")) {
this.addObserver(`content.[]`, this, this._compute);
}
+
+ if (this.get("isAsync")) {
+ this.addObserver(`asyncContent.[]`, this, this._compute);
+ }
},
willDestroyElement() {
this.removeObserver(`content.@each.${this.get("nameProperty")}`, this, this._compute);
this.removeObserver(`content.[]`, this, this._compute);
+ this.removeObserver(`asyncContent.[]`, this, this._compute);
},
willComputeAttributes() {},
@@ -114,6 +118,17 @@ export default Ember.Component.extend(UtilsMixin, PluginApiMixin, DomHelpersMixi
},
didComputeContent() {},
+ willComputeAsyncContent(content) { return content; },
+ computeAsyncContent(content) { return content; },
+ _beforeDidComputeAsyncContent(content) {
+ content = applyContentPluginApiCallbacks(this.get("pluginApiIdentifiers"), content, this);
+ this.setProperties({
+ computedAsyncContent: content.map(c => this.computeAsyncContentItem(c))
+ });
+ return content;
+ },
+ didComputeAsyncContent() {},
+
computeHeaderContent() {
return this.baseHeaderComputedContent();
},
@@ -122,6 +137,15 @@ export default Ember.Component.extend(UtilsMixin, PluginApiMixin, DomHelpersMixi
return this.baseComputedContentItem(contentItem, options);
},
+ computeAsyncContentItem(contentItem, options) {
+ return this.computeContentItem(contentItem, options);
+ },
+
+ @computed("isAsync", "filteredAsyncComputedContent", "filteredComputedContent")
+ collectionComputedContent(isAsync, filteredAsyncComputedContent, filteredComputedContent) {
+ return isAsync ? filteredAsyncComputedContent : filteredComputedContent;
+ },
+
validateCreate() { return true; },
validateSelect() { return true; },
@@ -155,13 +179,20 @@ export default Ember.Component.extend(UtilsMixin, PluginApiMixin, DomHelpersMixi
return false;
},
- @computed("filter", "filteredComputedContent.[]")
- noContentRow(filter, filteredComputedContent) {
- if (filter.length > 0 && filteredComputedContent.length === 0) {
+ @computed("filter", "collectionComputedContent.[]")
+ noContentRow(filter, collectionComputedContent) {
+ if (filter.length > 0 && collectionComputedContent.length === 0) {
return I18n.t("select_kit.no_content");
}
},
+ @computed("limitReached", "limit")
+ maxContentRow(limitReached, limit) {
+ if (limitReached) {
+ return I18n.t("select_kit.max_content_reached", { count: limit });
+ }
+ },
+
@computed("filter", "filterable", "autoFilterable", "renderedFilterOnce")
shouldFilter(filter, filterable, autoFilterable, renderedFilterOnce) {
if (renderedFilterOnce && filterable) return true;
@@ -170,8 +201,9 @@ export default Ember.Component.extend(UtilsMixin, PluginApiMixin, DomHelpersMixi
return false;
},
- @computed("filter", "computedContent")
- shouldDisplayCreateRow(filter, computedContent) {
+ @computed("filter", "computedContent", "limitReached")
+ shouldDisplayCreateRow(filter, computedContent, limitReached) {
+ if (limitReached) return false;
if (computedContent.map(c => c.value).includes(filter)) return false;
if (this.get("allowAny") && filter.length > 0 && this.validateCreate(filter)) return true;
return false;
diff --git a/app/assets/javascripts/select-kit/components/select-kit/select-kit-filter.js.es6 b/app/assets/javascripts/select-kit/components/select-kit/select-kit-filter.js.es6
index fbfed55bc3d..60e7ec98199 100644
--- a/app/assets/javascripts/select-kit/components/select-kit/select-kit-filter.js.es6
+++ b/app/assets/javascripts/select-kit/components/select-kit/select-kit-filter.js.es6
@@ -1,6 +1,15 @@
+import computed from "ember-addons/ember-computed-decorators";
+const { isEmpty } = Ember;
+
export default Ember.Component.extend({
layoutName: "select-kit/templates/components/select-kit/select-kit-filter",
classNames: ["select-kit-filter"],
classNameBindings: ["isFocused", "isHidden"],
- isHidden: Ember.computed.not("shouldDisplayFilter")
+ isHidden: Ember.computed.not("shouldDisplayFilter"),
+
+ @computed("placeholder", "hasSelection")
+ computedPlaceholder(placeholder, hasSelection) {
+ if (hasSelection) return "";
+ return isEmpty(placeholder) ? "" : I18n.t(placeholder);
+ }
});
diff --git a/app/assets/javascripts/select-kit/components/single-select.js.es6 b/app/assets/javascripts/select-kit/components/single-select.js.es6
index d442a9e2ae7..e859effe406 100644
--- a/app/assets/javascripts/select-kit/components/single-select.js.es6
+++ b/app/assets/javascripts/select-kit/components/single-select.js.es6
@@ -4,6 +4,7 @@ const { get, isNone, isEmpty, isPresent, run } = Ember;
export default SelectKitComponent.extend({
pluginApiIdentifiers: ["single-select"],
+ layoutName: "select-kit/templates/components/single-select",
classNames: "single-select",
computedValue: null,
value: null,
@@ -13,14 +14,20 @@ export default SelectKitComponent.extend({
_compute() {
run.scheduleOnce("afterRender", () => {
this.willComputeAttributes();
- let content = this.willComputeContent(this.get("content") || []);
+ let content = this.get("content") || [];
+ let asyncContent = this.get("asyncContent") || [];
+ content = this.willComputeContent(content);
+ asyncContent = this.willComputeAsyncContent(asyncContent);
let value = this._beforeWillComputeValue(this.get("value"));
content = this.computeContent(content);
+ asyncContent = this.computeAsyncContent(asyncContent);
content = this._beforeDidComputeContent(content);
+ asyncContent = this._beforeDidComputeAsyncContent(asyncContent);
value = this.willComputeValue(value);
value = this.computeValue(value);
value = this._beforeDidComputeValue(value);
this.didComputeContent(content);
+ this.didComputeAsyncContent(asyncContent);
this.didComputeValue(value);
this.didComputeAttributes();
@@ -86,6 +93,19 @@ export default SelectKitComponent.extend({
};
},
+ @computed("computedAsyncContent.[]", "computedValue")
+ filteredAsyncComputedContent(computedAsyncContent, computedValue) {
+ computedAsyncContent = computedAsyncContent.filter(c => {
+ return computedValue !== get(c, "value");
+ });
+
+ if (this.get("limitMatches")) {
+ return computedAsyncContent.slice(0, this.get("limitMatches"));
+ }
+
+ return computedAsyncContent;
+ },
+
@computed("computedContent.[]", "computedValue", "filter", "shouldFilter")
filteredComputedContent(computedContent, computedValue, filter, shouldFilter) {
if (shouldFilter) {
diff --git a/app/assets/javascripts/select-kit/components/tag-chooser.js.es6 b/app/assets/javascripts/select-kit/components/tag-chooser.js.es6
new file mode 100644
index 00000000000..c79e88bab02
--- /dev/null
+++ b/app/assets/javascripts/select-kit/components/tag-chooser.js.es6
@@ -0,0 +1,107 @@
+import MultiSelectComponent from "select-kit/components/multi-select";
+import Tags from "select-kit/mixins/tags";
+import renderTag from "discourse/lib/render-tag";
+import computed from "ember-addons/ember-computed-decorators";
+const { get, isEmpty, run, makeArray } = Ember;
+
+export default MultiSelectComponent.extend(Tags, {
+ pluginApiIdentifiers: ["tag-chooser"],
+ classNames: "tag-chooser",
+ isAsync: true,
+ filterable: true,
+ filterPlaceholder: "tagging.choose_for_topic",
+ limit: null,
+ attributeBindings: ["categoryId"],
+ allowAny: Ember.computed.alias("allowCreate"),
+
+ init() {
+ this._super();
+
+ if (this.get("allowCreate") !== false) {
+ this.set("allowCreate", this.get("siteSettings.can_create_tag"));
+ }
+
+ this.set("termMatchesForbidden", false);
+
+ this.set("templateForRow", (rowComponent) => {
+ const tag = rowComponent.get("computedContent");
+ return renderTag(get(tag, "value"), {
+ count: get(tag, "originalContent.count"),
+ noHref: true
+ });
+ });
+
+ if (!this.get("unlimitedTagCount")) {
+ this.set("limit", parseInt(this.get("limit") || this.get("siteSettings.max_tags_per_topic")));
+ }
+ },
+
+ mutateValues(values) {
+ this.set("tags", values.filter(v => v));
+ },
+
+ @computed("tags")
+ values(tags) {
+ return makeArray(tags);
+ },
+
+ @computed("tags")
+ content(tags) {
+ return makeArray(tags);
+ },
+
+ actions: {
+ onFilter(filter) {
+ this.expand();
+ this.set("searchDebounce", run.debounce(this, this.prepareSearch, filter, 200));
+ },
+
+ onExpand() {
+ if (isEmpty(this.get("collectionComputedContent"))) {
+ this.set("searchDebounce", run.debounce(this, this.prepareSearch, this.get("filter"), 200));
+ }
+ },
+
+ onDeselect() {
+ this.set("searchDebounce", run.debounce(this, this.prepareSearch, this.get("filter"), 200));
+ },
+
+ onSelect() {
+ this.set("searchDebounce", run.debounce(this, this.prepareSearch, this.get("filter"), 50));
+ }
+ },
+
+ prepareSearch(query) {
+ const selectedTags = makeArray(this.get("values")).filter(t => t);
+
+ const data = {
+ q: query,
+ limit: this.get("siteSettings.max_tag_search_results"),
+ categoryId: this.get("categoryId")
+ };
+ if (selectedTags) data.selected_tags = selectedTags.slice(0, 100);
+ if (!this.get("everyTag")) data.filterForInput = true;
+
+ this.searchTags("/tags/filter/search", data, this._transformJson);
+ },
+
+ _transformJson(context, json) {
+ let results = json.results;
+
+ context.set("termMatchesForbidden", json.forbidden ? true : false);
+
+ if (context.get("blacklist")) {
+ results = results.filter(result => {
+ return !context.get("blacklist").includes(result.id);
+ });
+ }
+
+ if (context.get("siteSettings.tags_sort_alphabetically")) {
+ results = results.sort((a, b) => a.id > b.id);
+ }
+
+ return results.map(result => {
+ return { id: result.text, name: result.text, count: result.count };
+ });
+ }
+});
diff --git a/app/assets/javascripts/select-kit/components/tag-group-chooser.js.es6 b/app/assets/javascripts/select-kit/components/tag-group-chooser.js.es6
new file mode 100644
index 00000000000..2b16037f72c
--- /dev/null
+++ b/app/assets/javascripts/select-kit/components/tag-group-chooser.js.es6
@@ -0,0 +1,79 @@
+import MultiSelectComponent from "select-kit/components/multi-select";
+import Tags from "select-kit/mixins/tags";
+import renderTag from "discourse/lib/render-tag";
+import computed from "ember-addons/ember-computed-decorators";
+const { get, isEmpty, run, makeArray } = Ember;
+
+export default MultiSelectComponent.extend(Tags, {
+ pluginApiIdentifiers: ["tag-group-chooser"],
+ classNames: ["tag-group-chooser", "tag-chooser"],
+ isAsync: true,
+ filterable: true,
+ filterPlaceholder: "category.tag_groups_placeholder",
+ limit: null,
+ allowAny: false,
+
+ init() {
+ this._super();
+
+ this.set("templateForRow", (rowComponent) => {
+ const tag = rowComponent.get("computedContent");
+ return renderTag(get(tag, "value"), {
+ count: get(tag, "originalContent.count"),
+ noHref: true
+ });
+ });
+ },
+
+ mutateValues(values) {
+ this.set("tagGroups", values.filter(v => v));
+ },
+
+ @computed("tagGroups")
+ values(tagGroups) {
+ return makeArray(tagGroups);
+ },
+
+ @computed("tagGroups")
+ content(tagGroups) {
+ return makeArray(tagGroups);
+ },
+
+ actions: {
+ onFilter(filter) {
+ this.expand();
+ this.set("searchDebounce", run.debounce(this, this.prepareSearch, filter, 200));
+ },
+
+ onExpand() {
+ if (isEmpty(this.get("collectionComputedContent"))) {
+ this.set("searchDebounce", run.debounce(this, this.prepareSearch, this.get("filter"), 200));
+ }
+ },
+
+ onDeselect() {
+ this.set("searchDebounce", run.debounce(this, this.prepareSearch, this.get("filter"), 200));
+ },
+
+ onSelect() {
+ this.set("searchDebounce", run.debounce(this, this.prepareSearch, this.get("filter"), 50));
+ }
+ },
+
+ prepareSearch(query) {
+ const data = {
+ q: query,
+ limit: this.get("siteSettings.max_tag_search_results")
+ };
+
+ this.searchTags("/tags/filter/search", data, this._transformJson);
+ },
+
+ _transformJson(context, json) {
+ let results = json.results.sort((a, b) => a.id > b.id);
+
+ return results.map(result => {
+ return { id: result.text, name: result.text, count: result.count };
+ });
+ },
+});
diff --git a/app/assets/javascripts/select-kit/mixins/tags.js.es6 b/app/assets/javascripts/select-kit/mixins/tags.js.es6
new file mode 100644
index 00000000000..8d2b2da3b65
--- /dev/null
+++ b/app/assets/javascripts/select-kit/mixins/tags.js.es6
@@ -0,0 +1,52 @@
+const { run } = Ember;
+import { ajax } from "discourse/lib/ajax";
+import { popupAjaxError } from "discourse/lib/ajax-error";
+
+export default Ember.Mixin.create({
+ willDestroyElement() {
+ this._super();
+
+ const searchDebounce = this.get("searchDebounce");
+ if (searchDebounce) run.cancel(searchDebounce);
+ },
+
+ searchTags(url, data, callback) {
+ const self = this;
+
+ this.startLoading();
+
+ return ajax(Discourse.getURL(url), {
+ quietMillis: 200,
+ cache: true,
+ dataType: "json",
+ data
+ }).then(json => {
+ self.set("asyncContent", callback(self, json));
+ }).catch(error => {
+ popupAjaxError(error);
+ })
+ .finally(() => {
+ self.stopLoading();
+ self.autoHighlight();
+ });
+ },
+
+ validateCreate(term) {
+ if (this.get("limitReached") || !this.site.get("can_create_tag")) {
+ return false;
+ }
+
+ const filterRegexp = new RegExp(this.site.tags_filter_regexp, "g");
+ term = term.replace(filterRegexp, "").trim().toLowerCase();
+
+ if (!term.length || this.get("termMatchesForbidden")) {
+ return false;
+ }
+
+ if (this.get("siteSettings.max_tag_length") < term.length) {
+ return false;
+ }
+
+ return true;
+ },
+});
diff --git a/app/assets/javascripts/select-kit/templates/components/multi-select.hbs b/app/assets/javascripts/select-kit/templates/components/multi-select.hbs
new file mode 100644
index 00000000000..072a4bff866
--- /dev/null
+++ b/app/assets/javascripts/select-kit/templates/components/multi-select.hbs
@@ -0,0 +1,50 @@
+{{#component headerComponent
+ tabindex=tabindex
+ isFocused=isFocused
+ isExpanded=isExpanded
+ computedContent=headerComputedContent
+ deselect=(action "deselect")
+ toggle=(action "toggle")
+ clearSelection=(action "clearSelection")
+ options=headerComponentOptions
+}}
+ {{component filterComponent
+ icon=filterIcon
+ placeholder=filterPlaceholder
+ filter=filter
+ hasSelection=hasSelection
+ isLoading=isLoading
+ shouldDisplayFilter=shouldDisplayFilter
+ isFocused=isFocused
+ filterComputedContent=(action "filterComputedContent")
+ }}
+{{/component}}
+
+
+ {{#if renderedBodyOnce}}
+ {{component collectionComponent
+ collectionHeaderComputedContent=collectionHeaderComputedContent
+ hasSelection=hasSelection
+ noneRowComputedContent=noneRowComputedContent
+ createRowComputedContent=createRowComputedContent
+ collectionComputedContent=collectionComputedContent
+ rowComponent=rowComponent
+ noneRowComponent=noneRowComponent
+ createRowComponent=createRowComponent
+ templateForRow=templateForRow
+ templateForNoneRow=templateForNoneRow
+ templateForCreateRow=templateForCreateRow
+ clearSelection=(action "clearSelection")
+ select=(action "select")
+ highlight=(action "highlight")
+ create=(action "create")
+ highlightedValue=highlightedValue
+ computedValue=computedValue
+ rowComponentOptions=rowComponentOptions
+ noContentRow=noContentRow
+ maxContentRow=maxContentRow
+ }}
+ {{/if}}
+
+
+
diff --git a/app/assets/javascripts/select-kit/templates/components/multi-select/multi-select-header.hbs b/app/assets/javascripts/select-kit/templates/components/multi-select/multi-select-header.hbs
index eb4aba3f06d..71173acb30d 100644
--- a/app/assets/javascripts/select-kit/templates/components/multi-select/multi-select-header.hbs
+++ b/app/assets/javascripts/select-kit/templates/components/multi-select/multi-select-header.hbs
@@ -5,11 +5,6 @@
computedContent=selectedComputedContent}}
{{/each}}
- {{component "select-kit/select-kit-filter"
- filterComputedContent=filterComputedContent
- shouldDisplayFilter=shouldDisplayFilter
- isFocused=isFocused
- filter=filter
- }}
+ {{yield}}
diff --git a/app/assets/javascripts/select-kit/templates/components/multi-select/selected-category.hbs b/app/assets/javascripts/select-kit/templates/components/multi-select/selected-category.hbs
index 6e2b1b362fd..09b5adfa859 100644
--- a/app/assets/javascripts/select-kit/templates/components/multi-select/selected-category.hbs
+++ b/app/assets/javascripts/select-kit/templates/components/multi-select/selected-category.hbs
@@ -1,7 +1,3 @@
diff --git a/app/assets/javascripts/select-kit/templates/components/multi-select/selected-name.hbs b/app/assets/javascripts/select-kit/templates/components/multi-select/selected-name.hbs
index e4167b47b4d..ffa6a8f3b91 100644
--- a/app/assets/javascripts/select-kit/templates/components/multi-select/selected-name.hbs
+++ b/app/assets/javascripts/select-kit/templates/components/multi-select/selected-name.hbs
@@ -1,16 +1,6 @@
{{#if headerContent}}{{/if}}
- {{#if isLocked}}
-
- {{d-icon "lock"}}
-
- {{else}}
-
- {{d-icon "times"}}
-
- {{/if}}
-
{{{label}}}
diff --git a/app/assets/javascripts/select-kit/templates/components/select-kit/select-kit-collection.hbs b/app/assets/javascripts/select-kit/templates/components/select-kit/select-kit-collection.hbs
index 35d46789dd3..12f5d053800 100644
--- a/app/assets/javascripts/select-kit/templates/components/select-kit/select-kit-collection.hbs
+++ b/app/assets/javascripts/select-kit/templates/components/select-kit/select-kit-collection.hbs
@@ -40,7 +40,7 @@
{{noContentRow}}
{{else}}
- {{#each filteredComputedContent as |computedContent|}}
+ {{#each collectionComputedContent as |computedContent|}}
{{component rowComponent
computedContent=computedContent
highlightedValue=highlightedValue
diff --git a/app/assets/javascripts/select-kit/templates/components/select-kit/select-kit-filter.hbs b/app/assets/javascripts/select-kit/templates/components/select-kit/select-kit-filter.hbs
index e7d1e96a7ef..dfe505f5b1f 100644
--- a/app/assets/javascripts/select-kit/templates/components/select-kit/select-kit-filter.hbs
+++ b/app/assets/javascripts/select-kit/templates/components/select-kit/select-kit-filter.hbs
@@ -1,7 +1,7 @@
{{input
tabindex=-1
class="filter-input"
- placeholder=placeholder
+ placeholder=computedPlaceholder
key-up=filterComputedContent
autocomplete="off"
autocorrect="off"
diff --git a/app/assets/javascripts/select-kit/templates/components/select-kit.hbs b/app/assets/javascripts/select-kit/templates/components/single-select.hbs
similarity index 86%
rename from app/assets/javascripts/select-kit/templates/components/select-kit.hbs
rename to app/assets/javascripts/select-kit/templates/components/single-select.hbs
index cd92fb83122..8f676036031 100644
--- a/app/assets/javascripts/select-kit/templates/components/select-kit.hbs
+++ b/app/assets/javascripts/select-kit/templates/components/single-select.hbs
@@ -1,13 +1,10 @@
{{component headerComponent
tabindex=tabindex
- shouldDisplayFilter=shouldDisplayFilter
isFocused=isFocused
isExpanded=isExpanded
computedContent=headerComputedContent
deselect=(action "deselect")
toggle=(action "toggle")
- isLoading=isLoading
- filterComputedContent=(action "filterComputedContent")
clearSelection=(action "clearSelection")
options=headerComponentOptions
}}
@@ -17,8 +14,9 @@
filter=filter
isLoading=isLoading
icon=filterIcon
+ hasSelection=hasSelection
shouldDisplayFilter=shouldDisplayFilter
- placeholder=(i18n filterPlaceholder)
+ placeholder=filterPlaceholder
isFocused=isFocused
filterComputedContent=(action "filterComputedContent")
}}
@@ -29,7 +27,7 @@
hasSelection=hasSelection
noneRowComputedContent=noneRowComputedContent
createRowComputedContent=createRowComputedContent
- filteredComputedContent=filteredComputedContent
+ collectionComputedContent=collectionComputedContent
rowComponent=rowComponent
noneRowComponent=noneRowComponent
createRowComponent=createRowComponent
diff --git a/app/assets/javascripts/vendor.js b/app/assets/javascripts/vendor.js
index 6a7bd345eca..03accd59828 100644
--- a/app/assets/javascripts/vendor.js
+++ b/app/assets/javascripts/vendor.js
@@ -14,7 +14,6 @@
//= require bootstrap-dropdown.js
//= require bootstrap-modal.js
//= require bootstrap-transition.js
-//= require select2.js
//= require div_resizer
//= require caret_position
//= require favcount.js
diff --git a/app/assets/javascripts/wizard-vendor.js b/app/assets/javascripts/wizard-vendor.js
index ecb8d2bd7e0..4d07d0ceea5 100644
--- a/app/assets/javascripts/wizard-vendor.js
+++ b/app/assets/javascripts/wizard-vendor.js
@@ -1,5 +1,4 @@
//= require template_include.js
-//= require select2.js
//= require jquery.ui.widget.js
//= require jquery.fileupload.js
//= require sweetalert.js
diff --git a/app/assets/stylesheets/common.scss b/app/assets/stylesheets/common.scss
index 565e4317447..9bf58300568 100644
--- a/app/assets/stylesheets/common.scss
+++ b/app/assets/stylesheets/common.scss
@@ -3,7 +3,6 @@
@import "vendor/pikaday";
@import "common/foundation/helpers";
@import "common/foundation/base";
-@import "vendor/select2";
@import "common/foundation/mixins";
@import "common/foundation/variables";
@import "common/select-kit/*";
diff --git a/app/assets/stylesheets/common/admin/admin_base.scss b/app/assets/stylesheets/common/admin/admin_base.scss
index c9f786c9b1f..1b199179aae 100644
--- a/app/assets/stylesheets/common/admin/admin_base.scss
+++ b/app/assets/stylesheets/common/admin/admin_base.scss
@@ -270,7 +270,7 @@ $mobile-breakpoint: 700px;
@include clearfix;
nav {
float: left;
- margin-left: 12px;
+ margin-left: 12px;
}
.nav.nav-pills {
li.active {
@@ -493,9 +493,6 @@ $mobile-breakpoint: 700px;
width: 100% !important; // !important overrides hard-coded mobile width of 68px
}
}
- .select2-container-multi .select2-choices {
- border: none;
- }
}
.setting-controls {
float: left;
@@ -521,13 +518,6 @@ $mobile-breakpoint: 700px;
border-radius: 3px;
transition: border linear 0.2s, box-shadow linear 0.2s;
- li.select2-search-choice {
- cursor: pointer;
- .select2-search-choice-close {
- content: "x"
- }
- }
-
li.sortable-placeholder {
padding: 3px 5px 3px 18px;
margin: 3px 0 3px 5px;
@@ -831,14 +821,6 @@ section.details {
.controls {
margin-top: 10px;
}
- .select2-container {
- width: 100%;
- }
- .select2-choices {
- width: 100%;
- border-color: dark-light-choose($primary-low-mid, $secondary-high);
- }
-
.content-list {
margin-right: 20px;
}
diff --git a/app/assets/stylesheets/common/admin/customize.scss b/app/assets/stylesheets/common/admin/customize.scss
index e3204d60f5b..841e4d4d991 100644
--- a/app/assets/stylesheets/common/admin/customize.scss
+++ b/app/assets/stylesheets/common/admin/customize.scss
@@ -36,7 +36,7 @@
.admin-footer {
margin-top: 20px;
}
- .select2-chosen, .color-schemes li {
+ .color-schemes li {
.fa {
margin-right: 6px;
color: dark-light-choose($primary-medium, $secondary-medium);
diff --git a/app/assets/stylesheets/common/base/compose.scss b/app/assets/stylesheets/common/base/compose.scss
index 2504fbd97ac..e050f69ceff 100644
--- a/app/assets/stylesheets/common/base/compose.scss
+++ b/app/assets/stylesheets/common/base/compose.scss
@@ -229,10 +229,6 @@
margin: 0;
flex: 1 1 100%;
}
-
- &.select2-dropdown-open, &.select2-container-active {
- border-color: $tertiary;
- }
}
.wmd-controls {
diff --git a/app/assets/stylesheets/common/base/modal.scss b/app/assets/stylesheets/common/base/modal.scss
index 2195c8e0186..f4c70b4b193 100644
--- a/app/assets/stylesheets/common/base/modal.scss
+++ b/app/assets/stylesheets/common/base/modal.scss
@@ -88,6 +88,10 @@
.select-kit {
width: 220px;
+
+ &.tag-chooser {
+ width: 100%;
+ }
}
}
diff --git a/app/assets/stylesheets/common/base/search.scss b/app/assets/stylesheets/common/base/search.scss
index cf5b0e4b9a4..46e6bb36f62 100644
--- a/app/assets/stylesheets/common/base/search.scss
+++ b/app/assets/stylesheets/common/base/search.scss
@@ -92,7 +92,7 @@
}
.search-bar {
- display: flex;
+ display: flex;
margin-bottom: 10px;
max-width: 780px;
input {
@@ -122,7 +122,7 @@
position: relative;
margin: 10px 0 15px;
max-width: 780px;
- border-bottom: 3px solid $primary-low;
+ border-bottom: 3px solid $primary-low;
width: 100%;
.term {
font-weight: bold;
@@ -130,7 +130,7 @@
.result-count {
float: left;
- margin-bottom: 4px;
+ margin-bottom: 4px;
span {
line-height: $line-height-large;
height: 28px;
@@ -163,7 +163,7 @@
max-width: 780px;
.search-advanced-options {
border: 1px solid $primary-low;
- padding: 0 20px;
+ padding: 0 20px;
width: 100%;
.date-picker-wrapper {
vertical-align: top;
@@ -183,6 +183,10 @@
font-weight: bold;
}
+ .tag-chooser {
+ width: 70%;
+ }
+
.container {
display: flex;
flex: 1 1 100%;
@@ -206,4 +210,4 @@
}
}
}
-}
\ No newline at end of file
+}
diff --git a/app/assets/stylesheets/common/base/tagging.scss b/app/assets/stylesheets/common/base/tagging.scss
index 11248496fcc..eb93138ccf7 100644
--- a/app/assets/stylesheets/common/base/tagging.scss
+++ b/app/assets/stylesheets/common/base/tagging.scss
@@ -25,8 +25,8 @@
.tag-count {
font-size: $font-down-1;
- vertical-align: middle;
- line-height: $line-height-small;
+ vertical-align: middle;
+ line-height: $line-height-small;
}
}
@@ -61,7 +61,7 @@
&.box, &.bullet {
}
-
+
&.box + .topic-header-extra,
&.bullet + .topic-header-extra,
&.bar + .topic-header-extra {
@@ -69,25 +69,17 @@
}
}
-.add-tags .select2 {
- margin: 0;
-}
-
$tag-color: $primary-medium;
.discourse-tag-count {
font-size: $font-down-1;
color: $tag-color;
line-height: $line-height-small;
- vertical-align: middle;
-}
-
-.select2-result-label .discourse-tag {
- margin-right: 0;
+ vertical-align: middle;
}
.discourse-tag {
- max-width: 14em;
+ max-width: 14em;
display: inline-block;
white-space: nowrap;
overflow: hidden;
@@ -131,29 +123,13 @@ $tag-color: $primary-medium;
}
.d-header .topic-header-extra {
- .discourse-tags {
+ .discourse-tags {
display: inline-block;
- font-size: $font-down-1;
+ font-size: $font-down-1;
}
.topic-featured-link { margin-left: 8px; }
}
-.select2-container-multi .select2-choices .select2-search-choice.discourse-tag-select2 {
- -webkit-box-shadow: none;
- box-shadow: none;
- border: 0;
- border-radius: 0;
- background-color: transparent;
- .discourse-tag {
- padding: 4px;
- &.box {
- padding: 1px 8px;
- margin: 3px 5px;
- }
- }
-}
-
-
.fps-result .add-full-page-tags {
display: inline-block;
}
@@ -213,11 +189,6 @@ header .discourse-tag {color: $tag-color }
width: 100%;
max-width: 100%;
margin: 5px 0;
- ul.select2-choices {
- max-height: 30px;
- padding-left: 10px;
- overflow-y: auto;
- }
}
.title-wrapper .tag-chooser {
@@ -278,11 +249,7 @@ header .discourse-tag {color: $tag-color }
}
}
.group-tags-list .tag-chooser {
- height: 250px !important;
- ul.select2-choices {
- height: 250px !important; // to fight with select2.scss's important
- max-height: none;
- }
+ width: 100%;
}
.btn {margin-left: 10px;}
.saving {
diff --git a/app/assets/stylesheets/common/base/topic.scss b/app/assets/stylesheets/common/base/topic.scss
index e0cfaa11e2c..0a76f198ab6 100644
--- a/app/assets/stylesheets/common/base/topic.scss
+++ b/app/assets/stylesheets/common/base/topic.scss
@@ -105,15 +105,15 @@ a.badge-category {
.private-message-glyph {
margin: 5px 5px 0 0;
}
- .category-chooser, .tag-chooser {
+ .category-chooser, .mini-tag-chooser {
flex: 1 1 49%;
margin: 0 0 9px 0;
@media all and (max-width: 500px) {
- flex: 1 1 100%;
+ flex: 1 1 100%;
}
-
+
}
- .tag-chooser {
+ .mini-tag-chooser {
margin-left: 2%;
@media all and (max-width: 500px) {
margin-left: 0;
@@ -204,7 +204,7 @@ a.badge-category {
.post-links {
margin-top: 1em;
padding-top: 1em;
- border-top: 1px solid $primary-low;
+ border-top: 1px solid $primary-low;
li:last-of-type {
margin-bottom: 1em;
}
diff --git a/app/assets/stylesheets/common/base/user.scss b/app/assets/stylesheets/common/base/user.scss
index 59c235e5be5..4fedecbc544 100644
--- a/app/assets/stylesheets/common/base/user.scss
+++ b/app/assets/stylesheets/common/base/user.scss
@@ -198,9 +198,9 @@
.staff-counters {
background: $primary;
- color: $secondary;
+ color: $secondary;
display: flex;
- padding: 10px;
+ padding: 10px;
> div, > div a {
display: flex;
align-items: baseline;
@@ -209,9 +209,9 @@
span {
padding: 1px 6px;
border-radius: 10px;
- margin-right: 5px;
+ margin-right: 5px;
}
- }
+ }
a {
color: $secondary;
@@ -404,7 +404,7 @@
&.linked-stat { // This makes the entire "box" (the li) clickable instead of a narrow area.
padding: 0;
a {
- padding: 10px 14px;
+ padding: 10px 14px;
width: 100%;
height: 100%;
display: block;
@@ -519,15 +519,6 @@
.tag-notifications .tag-controls {
margin-top: 24px;
}
-
- .tags .select2-container-multi {
- border: 1px solid $primary-low;
- width: 540px;
- border-radius: 0;
- .select2-choices {
- border: none;
- }
- }
}
.paginated-topics-list {
diff --git a/app/assets/stylesheets/common/select-kit/legacy-combo-box.scss b/app/assets/stylesheets/common/select-kit/legacy-combo-box.scss
deleted file mode 100644
index 2b72735d4ca..00000000000
--- a/app/assets/stylesheets/common/select-kit/legacy-combo-box.scss
+++ /dev/null
@@ -1,88 +0,0 @@
-// DO NOT MODIFY
-// TODO: remove when all select2 instances are gone
-.select2-results .select2-highlighted {
- background: $highlight-medium;
- color: $primary;
-}
-
-.select2-drop {
- .badge-category {
- display: inline-block;
- }
- .topic-count {
- font-size: $font-down-2;
- color: $primary;
- display: inline-block;
- }
- .highlighted .topic-count, .select2-highlighted .category-desc {
- color: $primary;
- }
- .category-desc {
- color: $primary;
- font-size: $font-down-1;
- line-height: 16px;
- }
-}
-
-.select2-drop {
- background: $secondary;
- .d-icon {
- color: dark-light-choose($primary-medium, $secondary-medium);
- }
-}
-
-.select2-search input {
- background: image-url("select2.png") no-repeat 100% -22px, $secondary 0 0
-}
-
-.select2-container {
- min-width: 200px;
-
- &.select2-dropdown-open {
- border: 0;
- margin-bottom: 2px;
- }
- &.select2-container-active {
- border-color: $tertiary;
- }
- &.select2-container-disabled .select2-chosen {
- color: blend-primary-secondary(50%);
- }
-}
-
-.select2-container-multi .select2-choices .select2-search-field input.select2-active {
- background: $secondary image-url("select2-spinner.gif") no-repeat 100% !important;
-}
-
-.select2-container-multi .select2-choices {
- border: 1px solid $primary-medium;
-}
-
-.select2-container a.select2-choice {
- background: $secondary;
- border-radius: 3px;
- border-color: $secondary;
- color: $primary;
-}
-
-.select2-dropdown-open a.select2-choice {
- box-shadow: none;
- border-radius: 3px 3px 0 0;
- border-color: $tertiary;
-}
-.select2-drop {
- color: $primary;
-}
-.select2-drop-active {
- border: 1px solid $tertiary;
- border-top: 0;
-}
-
-.select2-container-active {
- box-shadow: shadow("focus");
-}
-
-.select2-results .select2-no-results, .select2-results .select2-searching, .select2-results .select2-selection-limit {
- background: $secondary;
- color: $primary;
-}
diff --git a/app/assets/stylesheets/common/select-kit/mini-tag-chooser.scss b/app/assets/stylesheets/common/select-kit/mini-tag-chooser.scss
index 362e9b187af..27c8f44fc9e 100644
--- a/app/assets/stylesheets/common/select-kit/mini-tag-chooser.scss
+++ b/app/assets/stylesheets/common/select-kit/mini-tag-chooser.scss
@@ -7,7 +7,6 @@
&.is-expanded {
.select-kit-header {
border: 1px solid $tertiary;
- box-shadow: shadow("focus");
}
}
@@ -59,7 +58,7 @@
}
.selected-tag {
- background: $primary-very-low;
+ background: $primary-low;
padding: 2px 4px;
margin: 2px;
border: 0;
diff --git a/app/assets/stylesheets/common/select-kit/multi-select.scss b/app/assets/stylesheets/common/select-kit/multi-select.scss
index db3734bfb63..91ef4c15395 100644
--- a/app/assets/stylesheets/common/select-kit/multi-select.scss
+++ b/app/assets/stylesheets/common/select-kit/multi-select.scss
@@ -17,6 +17,7 @@
.select-kit-filter {
border: 0;
+ flex: 1;
}
.multi-select-header {
@@ -79,7 +80,7 @@
justify-content: space-between;
flex-wrap: wrap;
flex-direction: row;
- margin: 2.5px;
+ margin: 2px;
}
.filter {
@@ -89,6 +90,7 @@
min-width: 50px;
padding: 0;
outline: none;
+ flex: 1;
.filter-input, .filter-input:focus {
border: none;
@@ -117,17 +119,36 @@
.color-preview {
height: 5px;
margin: 0 2px 2px 2px;
- border-radius: 5px;
display: flex;
width: 100%;
}
}
+ .selected-category {
+ .badge-wrapper {
+ &.bullet {
+ margin-right: 2.5px;
+ }
+
+ margin: auto 2.5px;
+ padding: 2px 4px;
+ line-height: $line-height-medium;
+ display: flex;
+ flex: 1;
+ align-items: center;
+
+ &:after {
+ content: '\f00d';
+ color: $primary-low-mid;
+ font-family: 'FontAwesome';
+ font-size: $font-down-2;
+ margin-left: 5px;
+ }
+ }
+ }
+
.selected-name {
color: $primary;
- border: 1px solid $primary-medium;
- border-radius: 3px;
- box-shadow: 0 0 2px $secondary inset, 0 1px 0 rgba(0,0,0,0.05);
background-clip: padding-box;
-webkit-touch-callout: none;
user-select: none;
@@ -148,55 +169,32 @@
}
.body {
- width: 100%;
- display: inline-flex;
+ display: flex;
align-items: center;
-
- .locked-icon, .delete-icon {
- justify-content: center;
- align-items: center;
- display: inline-flex;
- height: 21px;
- width: 21px;
-
- .d-icon {
- color: $primary-medium;
- cursor: pointer;
- font-size: 1em;
- margin: 0;
-
- &:hover {
- color: $primary;
- }
- }
- }
+ flex: 1;
}
.name {
- padding: 0 5px;
+ padding: 2px 4px;
line-height: $line-height-medium;
+
+ &:after {
+ content: '\f00d';
+ color: $primary-low-mid;
+ font-family: 'FontAwesome';
+ font-size: $font-down-2;
+ }
+
+ &:hover {
+ &:after {
+ color: $danger;
+ }
+ }
}
&.is-highlighted {
box-shadow: 0 0 2px $danger, 0 1px 0 rgba(0,0,0,0.05);
}
-
- .locked-icon, .delete-icon {
- justify-content: center;
- align-items: center;
- width: 21px;
- height: 21px;
- display: inline-flex;
- .d-icon {
- color: $primary-medium;
- cursor: pointer;
- font-size: $font-0;
-
- &:hover {
- color: $primary;
- }
- }
- }
}
}
}
diff --git a/app/assets/stylesheets/common/select-kit/select-kit.scss b/app/assets/stylesheets/common/select-kit/select-kit.scss
index b5c1597ef32..3f61e5e2609 100644
--- a/app/assets/stylesheets/common/select-kit/select-kit.scss
+++ b/app/assets/stylesheets/common/select-kit/select-kit.scss
@@ -202,6 +202,7 @@
border-radius: inherit;
-webkit-overflow-scrolling: touch;
margin: 0;
+ padding: 0;
max-height: 200px;
.select-kit-collection {
diff --git a/app/assets/stylesheets/common/select-kit/tag-chooser.scss b/app/assets/stylesheets/common/select-kit/tag-chooser.scss
new file mode 100644
index 00000000000..cc51e23ef50
--- /dev/null
+++ b/app/assets/stylesheets/common/select-kit/tag-chooser.scss
@@ -0,0 +1,14 @@
+.select-kit {
+ &.multi-select {
+ &.tag-chooser {
+ .select-kit-row {
+ display: flex;
+ align-items: center;
+
+ .discourse-tag-count {
+ margin-left: 5px;
+ }
+ }
+ }
+ }
+}
diff --git a/app/assets/stylesheets/desktop/modal.scss b/app/assets/stylesheets/desktop/modal.scss
index 2a0fbf8d982..d52c0d48692 100644
--- a/app/assets/stylesheets/desktop/modal.scss
+++ b/app/assets/stylesheets/desktop/modal.scss
@@ -56,14 +56,6 @@
.category-combobox {
width: 430px;
-
- .select2-drop {
- left: -9000px;
- width: 428px;
- }
- .select2-search input {
- width: 378px;
- }
}
}
diff --git a/app/assets/stylesheets/desktop/queued-posts.scss b/app/assets/stylesheets/desktop/queued-posts.scss
index 79cfb1f6303..b4c5ef08883 100644
--- a/app/assets/stylesheets/desktop/queued-posts.scss
+++ b/app/assets/stylesheets/desktop/queued-posts.scss
@@ -24,6 +24,15 @@
}
}
+ .tag-chooser {
+ width: 100%;
+ margin-bottom: .5em;
+
+ .select-kit-collection {
+ padding: 0;
+ }
+ }
+
.queue-controls {
button {
float: left;
diff --git a/app/assets/stylesheets/desktop/topic.scss b/app/assets/stylesheets/desktop/topic.scss
index b4d33b09580..db173a36810 100644
--- a/app/assets/stylesheets/desktop/topic.scss
+++ b/app/assets/stylesheets/desktop/topic.scss
@@ -28,15 +28,12 @@
font-size: $font-up-4;
line-height: $line-height-medium;
overflow: hidden;
- width: 100%;
+ width: 100%;
a {color: $primary;}
}
.topic-statuses {
margin-top: -2px;
}
- .select2-container {
- vertical-align: middle;
- }
.private-message-glyph { display: none; }
.remove-featured-link {
float: right;
diff --git a/app/assets/stylesheets/desktop/user.scss b/app/assets/stylesheets/desktop/user.scss
index 0fb3e102f9f..d393e58994f 100644
--- a/app/assets/stylesheets/desktop/user.scss
+++ b/app/assets/stylesheets/desktop/user.scss
@@ -279,10 +279,12 @@
width: 530px;
}
+ .category-selector, .tag-chooser {
+ width: 530px;
+ }
+
input {
- &.category-selector,
- &.user-selector,
- &.tag-chooser {
+ &.user-selector {
width: 530px;
}
diff --git a/app/assets/stylesheets/mobile/topic.scss b/app/assets/stylesheets/mobile/topic.scss
index 703bba38ceb..48e682e147c 100644
--- a/app/assets/stylesheets/mobile/topic.scss
+++ b/app/assets/stylesheets/mobile/topic.scss
@@ -181,11 +181,6 @@ sup sup, sub sup, sup sub, sub sub { top: 0; }
width: 100%;
height: 32px;
}
-
- .select2-container {
- box-sizing: border-box;
- width: 100% !important;
- }
.btn-small {
padding: 6px 12px;
margin: 6px 6px 0 0;
diff --git a/app/assets/stylesheets/vendor/select2.scss b/app/assets/stylesheets/vendor/select2.scss
deleted file mode 100644
index 0981475b458..00000000000
--- a/app/assets/stylesheets/vendor/select2.scss
+++ /dev/null
@@ -1,575 +0,0 @@
-/*
-Version: @@ver@@ Timestamp: @@timestamp@@
-*/
-.select2-container {
- margin: 0;
- position: relative;
- display: inline-block;
- /* inline-block for ie7 */
- zoom: 1;
- 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;
-}
-
-.select2-container.select2-drop-above .select2-choice {
- border-bottom-color: #aaa;
-
- border-radius: 0 0 4px 4px;
-
-}
-
-.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;
-}
-
-.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: asset-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;
-
-
- -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;
-
-
- -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-radius: 0 4px 4px 0;
-
- background-clip: padding-box;
-
-}
-
-.select2-container .select2-choice .select2-arrow b {
- display: block;
- width: 100%;
- height: 100%;
- background: asset-url('select2.png') no-repeat 0 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;
-}
-
-//noinspection CssOverwrittenProperties
-.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 asset-url('select2.png') no-repeat 100% -22px;
- background: asset-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: asset-url('select2.png') no-repeat 100% -22px, -webkit-linear-gradient(center bottom, #fff 85%, #eee 99%);
- background: asset-url('select2.png') no-repeat 100% -22px, -moz-linear-gradient(center bottom, #fff 85%, #eee 99%);
- background: asset-url('select2.png') no-repeat 100% -22px, linear-gradient(to bottom, #fff 85%, #eee 99%) 0 0;
-}
-
-.select2-drop.select2-drop-above .select2-search input {
- margin-top: 4px;
-}
-
-//noinspection CssOverwrittenProperties
-.select2-search input.select2-active {
- background: #fff asset-url('select2-spinner.gif') no-repeat 100%;
- background: asset-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: asset-url('select2-spinner.gif') no-repeat 100%, -webkit-linear-gradient(center bottom, #fff 85%, #eee 99%);
- background: asset-url('select2-spinner.gif') no-repeat 100%, -moz-linear-gradient(center bottom, #fff 85%, #eee 99%);
- background: asset-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;
-}
-
-.select2-dropdown-open.select2-drop-above .select2-choice,
-.select2-dropdown-open.select2-drop-above .select2-choices {
- border: 1px solid #5897fb;
- border-top-color: transparent;
-}
-
-.select2-dropdown-open .select2-choice .select2-arrow {
- background: transparent;
- border-left: none;
- filter: none;
-}
-.select2-dropdown-open .select2-choice .select2-arrow b {
- background-position: -18px 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);
-}
-
-.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 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-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 asset-url('select2-spinner.gif') no-repeat 100%;
-}
-
-.select2-more-results {
- background: #f4f4f4;
- display: list-item;
-}
-
-/* disabled styles */
-
-.select2-container.select2-container-disabled .select2-choice {
- background: #f4f4f4 none;
- border: 1px solid #ddd;
- cursor: default;
-}
-
-.select2-container.select2-container-disabled .select2-choice .select2-arrow {
- background: #f4f4f4 none;
- border-left: 0;
-}
-
-.select2-container.select2-container-disabled .select2-choice abbr {
- display: none;
-}
-
-
-/* multiselect */
-
-.select2-container-multi .select2-choices {
- height: auto !important;
- margin: 0;
- padding: 0 5px 0 0;
- position: relative;
- cursor: text;
- overflow: hidden;
-}
-
-.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-left: 0;
- font-family: sans-serif;
- font-size: 1em;
- color: #666;
- outline: 0;
- border: 0;
- margin-bottom: 0;
- -webkit-box-shadow: none;
- box-shadow: none;
- background: transparent !important;
-}
-
-.select2-container-multi .select2-choices .select2-search-field input.select2-active {
- background: #fff asset-url('select2-spinner.gif') no-repeat 100% !important;
-}
-
-.select2-default {
- color: #999 !important;
-}
-
-.select2-container-multi .select2-choices .select2-search-choice {
- padding: 0 0 0 12px;
- margin: 0;
- position: relative;
-
- 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;
-}
-html[dir="rtl"] .select2-container-multi .select2-choices .select2-search-choice
-{
- margin-left: 0;
- margin-right: 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: 8px;
-
- font-size: 1px;
- outline: none;
- background: asset-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;
-}
-
-.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: #f4f4f4 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: #f4f4f4 none;
-}
-
-.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: 0 !important;
- top: 0 !important;
-}
-
-.select2-display-none {
- display: none;
-}
-
-.select2-measure-scrollbar {
- position: absolute;
- top: -10000px;
- left: -10000px;
- width: 100px;
- height: 100px;
- overflow: scroll;
-}
diff --git a/app/assets/stylesheets/wizard.scss b/app/assets/stylesheets/wizard.scss
index 821c8e86895..0fa4c95008c 100644
--- a/app/assets/stylesheets/wizard.scss
+++ b/app/assets/stylesheets/wizard.scss
@@ -1,6 +1,5 @@
@import "vendor/normalize";
@import "vendor/font_awesome/font-awesome";
-@import "vendor/select2";
@import "vendor/sweetalert";
@import "common/foundation/colors";
@import "common/foundation/variables";
@@ -63,9 +62,6 @@ body.wizard {
.select {
width: 400px;
}
-.select2-results .select2-highlighted {
- background: #ff9;
-}
.wizard-canvas {
position: absolute;
@@ -484,7 +480,6 @@ body.wizard {
.wizard-column { margin: auto !important; }
.wizard-step-contents { min-height: auto !important; }
.wizard-step-banner { width: 100% !important; margin-bottom: 1em !important; }
- .select2-container { width: 100% !important; }
.wizard-step-footer { display: block !important; }
.wizard-progress { margin-bottom: 10px !important; }
.wizard-buttons { text-align: right !important; }
diff --git a/app/views/finish_installation/register.html.erb b/app/views/finish_installation/register.html.erb
index 8fa60e3e380..cf6457da3d2 100644
--- a/app/views/finish_installation/register.html.erb
+++ b/app/views/finish_installation/register.html.erb
@@ -51,9 +51,3 @@
<%- else -%>
<%= raw(t 'finish_installation.register.no_emails') %>
<%- end %>
-
-
diff --git a/test/javascripts/acceptance/search-full-test.js.es6 b/test/javascripts/acceptance/search-full-test.js.es6
index dba53a92525..8027f64823b 100644
--- a/test/javascripts/acceptance/search-full-test.js.es6
+++ b/test/javascripts/acceptance/search-full-test.js.es6
@@ -75,30 +75,6 @@ QUnit.test("open advanced search", assert => {
andThen(() => assert.ok(visible('.search-advanced .search-advanced-options'), '"search-advanced-options" is visible'));
});
-// these tests are screwy with the runloop
-
-// test("validate population of advanced search", assert => {
-// visit("/search");
-// fillIn('.search input.full-page-search', 'test user:admin #bug group:moderators badge:Reader tags:monkey in:likes in:private in:wiki in:bookmarks status:open after:2016-10-05 min_post_count:10');
-// click('.search-advanced-btn');
-//
-// andThen(() => {
-// assert.ok(exists('.search-advanced-options span:contains("admin")'), 'has "admin" pre-populated');
-// assert.ok(exists('.search-advanced-options .badge-category:contains("bug")'), 'has "bug" pre-populated');
-// //assert.ok(exists('.search-advanced-options span:contains("moderators")'), 'has "moderators" pre-populated');
-// //assert.ok(exists('.search-advanced-options span:contains("Reader")'), 'has "Reader" pre-populated');
-// assert.ok(exists('.search-advanced-options .tag-chooser .tag-monkey'), 'has "monkey" pre-populated');
-// assert.ok(exists('.search-advanced-options .in-likes:checked'), 'has "I liked" pre-populated');
-// assert.ok(exists('.search-advanced-options .in-private:checked'), 'has "are in my messages" pre-populated');
-// assert.ok(exists('.search-advanced-options .in-wiki:checked'), 'has "are wiki" pre-populated');
-// assert.ok(exists('.search-advanced-options .combobox .select2-choice .select2-chosen:contains("I\'ve bookmarked")'), 'has "I\'ve bookmarked" pre-populated');
-// assert.ok(exists('.search-advanced-options .combobox .select2-choice .select2-chosen:contains("are open")'), 'has "are open" pre-populated');
-// assert.ok(exists('.search-advanced-options .combobox .select2-choice .select2-chosen:contains("after")'), 'has "after" pre-populated');
-// assert.equal(find('.search-advanced-options #search-post-date').val(), "2016-10-05", 'has "2016-10-05" pre-populated');
-// assert.equal(find('.search-advanced-options #search-min-post-count').val(), "10", 'has "10" pre-populated');
-// });
-// });
-
QUnit.test("escape search term", (assert) => {
visit("/search");
fillIn('.search input.full-page-search', '@gmail.com');
diff --git a/vendor/assets/javascripts/select2.js b/vendor/assets/javascripts/select2.js
deleted file mode 100644
index 195ccee5bb0..00000000000
--- a/vendor/assets/javascripts/select2.js
+++ /dev/null
@@ -1,3729 +0,0 @@
-/*
-Copyright 2012 Igor Vaynberg
-
-Version: 3.5.4 Timestamp: Sun Aug 30 13:30:32 EDT 2015
-
-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 License 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 ($) {
- if(typeof $.fn.each2 == "undefined") {
- $.extend($.fn, {
- /*
- * 4-10 times faster .each replacement
- * use it carefully, as it overrides jQuery context of element on each iteration
- */
- each2 : function (c) {
- var j = $([0]), i = -1, l = this.length;
- while (
- ++i < l
- && (j.context = j[0] = this[i])
- && c.call(j[0], i, j) !== false //"this"=DOM, i=index, j=jQuery object
- );
- return this;
- }
- });
- }
-})(jQuery);
-
-(function ($, undefined) {
- "use strict";
- /*global document, window, jQuery, console */
-
- if (window.Select2 !== undefined) {
- return;
- }
-
- var AbstractSelect2, SingleSelect2, MultiSelect2, nextUid, sizer,
- lastMousePosition={x:0,y:0}, $document, scrollBarDimensions,
-
- KEY = {
- TAB: 9,
- ENTER: 13,
- ESC: 27,
- SPACE: 32,
- LEFT: 37,
- UP: 38,
- RIGHT: 39,
- DOWN: 40,
- SHIFT: 16,
- CTRL: 17,
- ALT: 18,
- PAGE_UP: 33,
- PAGE_DOWN: 34,
- HOME: 36,
- END: 35,
- BACKSPACE: 8,
- DELETE: 46,
- isArrow: function (k) {
- k = k.which ? k.which : k;
- switch (k) {
- case KEY.LEFT:
- case KEY.RIGHT:
- case KEY.UP:
- case KEY.DOWN:
- return true;
- }
- return false;
- },
- isControl: function (e) {
- var k = e.which;
- switch (k) {
- case KEY.SHIFT:
- case KEY.CTRL:
- case KEY.ALT:
- return true;
- }
-
- if (e.metaKey) return true;
-
- return false;
- },
- isFunctionKey: function (k) {
- k = k.which ? k.which : k;
- return k >= 112 && k <= 123;
- }
- },
- MEASURE_SCROLLBAR_TEMPLATE = "
",
-
- DIACRITICS = {"\u24B6":"A","\uFF21":"A","\u00C0":"A","\u00C1":"A","\u00C2":"A","\u1EA6":"A","\u1EA4":"A","\u1EAA":"A","\u1EA8":"A","\u00C3":"A","\u0100":"A","\u0102":"A","\u1EB0":"A","\u1EAE":"A","\u1EB4":"A","\u1EB2":"A","\u0226":"A","\u01E0":"A","\u00C4":"A","\u01DE":"A","\u1EA2":"A","\u00C5":"A","\u01FA":"A","\u01CD":"A","\u0200":"A","\u0202":"A","\u1EA0":"A","\u1EAC":"A","\u1EB6":"A","\u1E00":"A","\u0104":"A","\u023A":"A","\u2C6F":"A","\uA732":"AA","\u00C6":"AE","\u01FC":"AE","\u01E2":"AE","\uA734":"AO","\uA736":"AU","\uA738":"AV","\uA73A":"AV","\uA73C":"AY","\u24B7":"B","\uFF22":"B","\u1E02":"B","\u1E04":"B","\u1E06":"B","\u0243":"B","\u0182":"B","\u0181":"B","\u24B8":"C","\uFF23":"C","\u0106":"C","\u0108":"C","\u010A":"C","\u010C":"C","\u00C7":"C","\u1E08":"C","\u0187":"C","\u023B":"C","\uA73E":"C","\u24B9":"D","\uFF24":"D","\u1E0A":"D","\u010E":"D","\u1E0C":"D","\u1E10":"D","\u1E12":"D","\u1E0E":"D","\u0110":"D","\u018B":"D","\u018A":"D","\u0189":"D","\uA779":"D","\u01F1":"DZ","\u01C4":"DZ","\u01F2":"Dz","\u01C5":"Dz","\u24BA":"E","\uFF25":"E","\u00C8":"E","\u00C9":"E","\u00CA":"E","\u1EC0":"E","\u1EBE":"E","\u1EC4":"E","\u1EC2":"E","\u1EBC":"E","\u0112":"E","\u1E14":"E","\u1E16":"E","\u0114":"E","\u0116":"E","\u00CB":"E","\u1EBA":"E","\u011A":"E","\u0204":"E","\u0206":"E","\u1EB8":"E","\u1EC6":"E","\u0228":"E","\u1E1C":"E","\u0118":"E","\u1E18":"E","\u1E1A":"E","\u0190":"E","\u018E":"E","\u24BB":"F","\uFF26":"F","\u1E1E":"F","\u0191":"F","\uA77B":"F","\u24BC":"G","\uFF27":"G","\u01F4":"G","\u011C":"G","\u1E20":"G","\u011E":"G","\u0120":"G","\u01E6":"G","\u0122":"G","\u01E4":"G","\u0193":"G","\uA7A0":"G","\uA77D":"G","\uA77E":"G","\u24BD":"H","\uFF28":"H","\u0124":"H","\u1E22":"H","\u1E26":"H","\u021E":"H","\u1E24":"H","\u1E28":"H","\u1E2A":"H","\u0126":"H","\u2C67":"H","\u2C75":"H","\uA78D":"H","\u24BE":"I","\uFF29":"I","\u00CC":"I","\u00CD":"I","\u00CE":"I","\u0128":"I","\u012A":"I","\u012C":"I","\u0130":"I","\u00CF":"I","\u1E2E":"I","\u1EC8":"I","\u01CF":"I","\u0208":"I","\u020A":"I","\u1ECA":"I","\u012E":"I","\u1E2C":"I","\u0197":"I","\u24BF":"J","\uFF2A":"J","\u0134":"J","\u0248":"J","\u24C0":"K","\uFF2B":"K","\u1E30":"K","\u01E8":"K","\u1E32":"K","\u0136":"K","\u1E34":"K","\u0198":"K","\u2C69":"K","\uA740":"K","\uA742":"K","\uA744":"K","\uA7A2":"K","\u24C1":"L","\uFF2C":"L","\u013F":"L","\u0139":"L","\u013D":"L","\u1E36":"L","\u1E38":"L","\u013B":"L","\u1E3C":"L","\u1E3A":"L","\u0141":"L","\u023D":"L","\u2C62":"L","\u2C60":"L","\uA748":"L","\uA746":"L","\uA780":"L","\u01C7":"LJ","\u01C8":"Lj","\u24C2":"M","\uFF2D":"M","\u1E3E":"M","\u1E40":"M","\u1E42":"M","\u2C6E":"M","\u019C":"M","\u24C3":"N","\uFF2E":"N","\u01F8":"N","\u0143":"N","\u00D1":"N","\u1E44":"N","\u0147":"N","\u1E46":"N","\u0145":"N","\u1E4A":"N","\u1E48":"N","\u0220":"N","\u019D":"N","\uA790":"N","\uA7A4":"N","\u01CA":"NJ","\u01CB":"Nj","\u24C4":"O","\uFF2F":"O","\u00D2":"O","\u00D3":"O","\u00D4":"O","\u1ED2":"O","\u1ED0":"O","\u1ED6":"O","\u1ED4":"O","\u00D5":"O","\u1E4C":"O","\u022C":"O","\u1E4E":"O","\u014C":"O","\u1E50":"O","\u1E52":"O","\u014E":"O","\u022E":"O","\u0230":"O","\u00D6":"O","\u022A":"O","\u1ECE":"O","\u0150":"O","\u01D1":"O","\u020C":"O","\u020E":"O","\u01A0":"O","\u1EDC":"O","\u1EDA":"O","\u1EE0":"O","\u1EDE":"O","\u1EE2":"O","\u1ECC":"O","\u1ED8":"O","\u01EA":"O","\u01EC":"O","\u00D8":"O","\u01FE":"O","\u0186":"O","\u019F":"O","\uA74A":"O","\uA74C":"O","\u01A2":"OI","\uA74E":"OO","\u0222":"OU","\u24C5":"P","\uFF30":"P","\u1E54":"P","\u1E56":"P","\u01A4":"P","\u2C63":"P","\uA750":"P","\uA752":"P","\uA754":"P","\u24C6":"Q","\uFF31":"Q","\uA756":"Q","\uA758":"Q","\u024A":"Q","\u24C7":"R","\uFF32":"R","\u0154":"R","\u1E58":"R","\u0158":"R","\u0210":"R","\u0212":"R","\u1E5A":"R","\u1E5C":"R","\u0156":"R","\u1E5E":"R","\u024C":"R","\u2C64":"R","\uA75A":"R","\uA7A6":"R","\uA782":"R","\u24C8":"S","\uFF33":"S","\u1E9E":"S","\u015A":"S","\u1E64":"S","\u015C":"S","\u1E60":"S","\u0160":"S","\u1E66":"S","\u1E62":"S","\u1E68":"S","\u0218":"S","\u015E":"S","\u2C7E":"S","\uA7A8":"S","\uA784":"S","\u24C9":"T","\uFF34":"T","\u1E6A":"T","\u0164":"T","\u1E6C":"T","\u021A":"T","\u0162":"T","\u1E70":"T","\u1E6E":"T","\u0166":"T","\u01AC":"T","\u01AE":"T","\u023E":"T","\uA786":"T","\uA728":"TZ","\u24CA":"U","\uFF35":"U","\u00D9":"U","\u00DA":"U","\u00DB":"U","\u0168":"U","\u1E78":"U","\u016A":"U","\u1E7A":"U","\u016C":"U","\u00DC":"U","\u01DB":"U","\u01D7":"U","\u01D5":"U","\u01D9":"U","\u1EE6":"U","\u016E":"U","\u0170":"U","\u01D3":"U","\u0214":"U","\u0216":"U","\u01AF":"U","\u1EEA":"U","\u1EE8":"U","\u1EEE":"U","\u1EEC":"U","\u1EF0":"U","\u1EE4":"U","\u1E72":"U","\u0172":"U","\u1E76":"U","\u1E74":"U","\u0244":"U","\u24CB":"V","\uFF36":"V","\u1E7C":"V","\u1E7E":"V","\u01B2":"V","\uA75E":"V","\u0245":"V","\uA760":"VY","\u24CC":"W","\uFF37":"W","\u1E80":"W","\u1E82":"W","\u0174":"W","\u1E86":"W","\u1E84":"W","\u1E88":"W","\u2C72":"W","\u24CD":"X","\uFF38":"X","\u1E8A":"X","\u1E8C":"X","\u24CE":"Y","\uFF39":"Y","\u1EF2":"Y","\u00DD":"Y","\u0176":"Y","\u1EF8":"Y","\u0232":"Y","\u1E8E":"Y","\u0178":"Y","\u1EF6":"Y","\u1EF4":"Y","\u01B3":"Y","\u024E":"Y","\u1EFE":"Y","\u24CF":"Z","\uFF3A":"Z","\u0179":"Z","\u1E90":"Z","\u017B":"Z","\u017D":"Z","\u1E92":"Z","\u1E94":"Z","\u01B5":"Z","\u0224":"Z","\u2C7F":"Z","\u2C6B":"Z","\uA762":"Z","\u24D0":"a","\uFF41":"a","\u1E9A":"a","\u00E0":"a","\u00E1":"a","\u00E2":"a","\u1EA7":"a","\u1EA5":"a","\u1EAB":"a","\u1EA9":"a","\u00E3":"a","\u0101":"a","\u0103":"a","\u1EB1":"a","\u1EAF":"a","\u1EB5":"a","\u1EB3":"a","\u0227":"a","\u01E1":"a","\u00E4":"a","\u01DF":"a","\u1EA3":"a","\u00E5":"a","\u01FB":"a","\u01CE":"a","\u0201":"a","\u0203":"a","\u1EA1":"a","\u1EAD":"a","\u1EB7":"a","\u1E01":"a","\u0105":"a","\u2C65":"a","\u0250":"a","\uA733":"aa","\u00E6":"ae","\u01FD":"ae","\u01E3":"ae","\uA735":"ao","\uA737":"au","\uA739":"av","\uA73B":"av","\uA73D":"ay","\u24D1":"b","\uFF42":"b","\u1E03":"b","\u1E05":"b","\u1E07":"b","\u0180":"b","\u0183":"b","\u0253":"b","\u24D2":"c","\uFF43":"c","\u0107":"c","\u0109":"c","\u010B":"c","\u010D":"c","\u00E7":"c","\u1E09":"c","\u0188":"c","\u023C":"c","\uA73F":"c","\u2184":"c","\u24D3":"d","\uFF44":"d","\u1E0B":"d","\u010F":"d","\u1E0D":"d","\u1E11":"d","\u1E13":"d","\u1E0F":"d","\u0111":"d","\u018C":"d","\u0256":"d","\u0257":"d","\uA77A":"d","\u01F3":"dz","\u01C6":"dz","\u24D4":"e","\uFF45":"e","\u00E8":"e","\u00E9":"e","\u00EA":"e","\u1EC1":"e","\u1EBF":"e","\u1EC5":"e","\u1EC3":"e","\u1EBD":"e","\u0113":"e","\u1E15":"e","\u1E17":"e","\u0115":"e","\u0117":"e","\u00EB":"e","\u1EBB":"e","\u011B":"e","\u0205":"e","\u0207":"e","\u1EB9":"e","\u1EC7":"e","\u0229":"e","\u1E1D":"e","\u0119":"e","\u1E19":"e","\u1E1B":"e","\u0247":"e","\u025B":"e","\u01DD":"e","\u24D5":"f","\uFF46":"f","\u1E1F":"f","\u0192":"f","\uA77C":"f","\u24D6":"g","\uFF47":"g","\u01F5":"g","\u011D":"g","\u1E21":"g","\u011F":"g","\u0121":"g","\u01E7":"g","\u0123":"g","\u01E5":"g","\u0260":"g","\uA7A1":"g","\u1D79":"g","\uA77F":"g","\u24D7":"h","\uFF48":"h","\u0125":"h","\u1E23":"h","\u1E27":"h","\u021F":"h","\u1E25":"h","\u1E29":"h","\u1E2B":"h","\u1E96":"h","\u0127":"h","\u2C68":"h","\u2C76":"h","\u0265":"h","\u0195":"hv","\u24D8":"i","\uFF49":"i","\u00EC":"i","\u00ED":"i","\u00EE":"i","\u0129":"i","\u012B":"i","\u012D":"i","\u00EF":"i","\u1E2F":"i","\u1EC9":"i","\u01D0":"i","\u0209":"i","\u020B":"i","\u1ECB":"i","\u012F":"i","\u1E2D":"i","\u0268":"i","\u0131":"i","\u24D9":"j","\uFF4A":"j","\u0135":"j","\u01F0":"j","\u0249":"j","\u24DA":"k","\uFF4B":"k","\u1E31":"k","\u01E9":"k","\u1E33":"k","\u0137":"k","\u1E35":"k","\u0199":"k","\u2C6A":"k","\uA741":"k","\uA743":"k","\uA745":"k","\uA7A3":"k","\u24DB":"l","\uFF4C":"l","\u0140":"l","\u013A":"l","\u013E":"l","\u1E37":"l","\u1E39":"l","\u013C":"l","\u1E3D":"l","\u1E3B":"l","\u017F":"l","\u0142":"l","\u019A":"l","\u026B":"l","\u2C61":"l","\uA749":"l","\uA781":"l","\uA747":"l","\u01C9":"lj","\u24DC":"m","\uFF4D":"m","\u1E3F":"m","\u1E41":"m","\u1E43":"m","\u0271":"m","\u026F":"m","\u24DD":"n","\uFF4E":"n","\u01F9":"n","\u0144":"n","\u00F1":"n","\u1E45":"n","\u0148":"n","\u1E47":"n","\u0146":"n","\u1E4B":"n","\u1E49":"n","\u019E":"n","\u0272":"n","\u0149":"n","\uA791":"n","\uA7A5":"n","\u01CC":"nj","\u24DE":"o","\uFF4F":"o","\u00F2":"o","\u00F3":"o","\u00F4":"o","\u1ED3":"o","\u1ED1":"o","\u1ED7":"o","\u1ED5":"o","\u00F5":"o","\u1E4D":"o","\u022D":"o","\u1E4F":"o","\u014D":"o","\u1E51":"o","\u1E53":"o","\u014F":"o","\u022F":"o","\u0231":"o","\u00F6":"o","\u022B":"o","\u1ECF":"o","\u0151":"o","\u01D2":"o","\u020D":"o","\u020F":"o","\u01A1":"o","\u1EDD":"o","\u1EDB":"o","\u1EE1":"o","\u1EDF":"o","\u1EE3":"o","\u1ECD":"o","\u1ED9":"o","\u01EB":"o","\u01ED":"o","\u00F8":"o","\u01FF":"o","\u0254":"o","\uA74B":"o","\uA74D":"o","\u0275":"o","\u01A3":"oi","\u0223":"ou","\uA74F":"oo","\u24DF":"p","\uFF50":"p","\u1E55":"p","\u1E57":"p","\u01A5":"p","\u1D7D":"p","\uA751":"p","\uA753":"p","\uA755":"p","\u24E0":"q","\uFF51":"q","\u024B":"q","\uA757":"q","\uA759":"q","\u24E1":"r","\uFF52":"r","\u0155":"r","\u1E59":"r","\u0159":"r","\u0211":"r","\u0213":"r","\u1E5B":"r","\u1E5D":"r","\u0157":"r","\u1E5F":"r","\u024D":"r","\u027D":"r","\uA75B":"r","\uA7A7":"r","\uA783":"r","\u24E2":"s","\uFF53":"s","\u00DF":"s","\u015B":"s","\u1E65":"s","\u015D":"s","\u1E61":"s","\u0161":"s","\u1E67":"s","\u1E63":"s","\u1E69":"s","\u0219":"s","\u015F":"s","\u023F":"s","\uA7A9":"s","\uA785":"s","\u1E9B":"s","\u24E3":"t","\uFF54":"t","\u1E6B":"t","\u1E97":"t","\u0165":"t","\u1E6D":"t","\u021B":"t","\u0163":"t","\u1E71":"t","\u1E6F":"t","\u0167":"t","\u01AD":"t","\u0288":"t","\u2C66":"t","\uA787":"t","\uA729":"tz","\u24E4":"u","\uFF55":"u","\u00F9":"u","\u00FA":"u","\u00FB":"u","\u0169":"u","\u1E79":"u","\u016B":"u","\u1E7B":"u","\u016D":"u","\u00FC":"u","\u01DC":"u","\u01D8":"u","\u01D6":"u","\u01DA":"u","\u1EE7":"u","\u016F":"u","\u0171":"u","\u01D4":"u","\u0215":"u","\u0217":"u","\u01B0":"u","\u1EEB":"u","\u1EE9":"u","\u1EEF":"u","\u1EED":"u","\u1EF1":"u","\u1EE5":"u","\u1E73":"u","\u0173":"u","\u1E77":"u","\u1E75":"u","\u0289":"u","\u24E5":"v","\uFF56":"v","\u1E7D":"v","\u1E7F":"v","\u028B":"v","\uA75F":"v","\u028C":"v","\uA761":"vy","\u24E6":"w","\uFF57":"w","\u1E81":"w","\u1E83":"w","\u0175":"w","\u1E87":"w","\u1E85":"w","\u1E98":"w","\u1E89":"w","\u2C73":"w","\u24E7":"x","\uFF58":"x","\u1E8B":"x","\u1E8D":"x","\u24E8":"y","\uFF59":"y","\u1EF3":"y","\u00FD":"y","\u0177":"y","\u1EF9":"y","\u0233":"y","\u1E8F":"y","\u00FF":"y","\u1EF7":"y","\u1E99":"y","\u1EF5":"y","\u01B4":"y","\u024F":"y","\u1EFF":"y","\u24E9":"z","\uFF5A":"z","\u017A":"z","\u1E91":"z","\u017C":"z","\u017E":"z","\u1E93":"z","\u1E95":"z","\u01B6":"z","\u0225":"z","\u0240":"z","\u2C6C":"z","\uA763":"z","\u0386":"\u0391","\u0388":"\u0395","\u0389":"\u0397","\u038A":"\u0399","\u03AA":"\u0399","\u038C":"\u039F","\u038E":"\u03A5","\u03AB":"\u03A5","\u038F":"\u03A9","\u03AC":"\u03B1","\u03AD":"\u03B5","\u03AE":"\u03B7","\u03AF":"\u03B9","\u03CA":"\u03B9","\u0390":"\u03B9","\u03CC":"\u03BF","\u03CD":"\u03C5","\u03CB":"\u03C5","\u03B0":"\u03C5","\u03C9":"\u03C9","\u03C2":"\u03C3"};
-
- $document = $(document);
-
- nextUid=(function() { var counter=1; return function() { return counter++; }; }());
-
-
- function reinsertElement(element) {
- var placeholder = $(document.createTextNode(''));
-
- element.before(placeholder);
- placeholder.before(element);
- placeholder.remove();
- }
-
- function stripDiacritics(str) {
- // Used 'uni range + named function' from http://jsperf.com/diacritics/18
- function match(a) {
- return DIACRITICS[a] || a;
- }
-
- return str.replace(/[^\u0000-\u007E]/g, match);
- }
-
- function indexOf(value, array) {
- var i = 0, l = array.length;
- for (; i < l; i = i + 1) {
- if (equal(value, array[i])) return i;
- }
- return -1;
- }
-
- function measureScrollbar () {
- var $template = $( MEASURE_SCROLLBAR_TEMPLATE );
- $template.appendTo(document.body);
-
- var dim = {
- width: $template.width() - $template[0].clientWidth,
- height: $template.height() - $template[0].clientHeight
- };
- $template.remove();
-
- return dim;
- }
-
- /**
- * Compares equality of a and b
- * @param a
- * @param b
- */
- function equal(a, b) {
- if (a === b) return true;
- if (a === undefined || b === undefined) return false;
- if (a === null || b === null) return false;
- // Check whether 'a' or 'b' is a string (primitive or object).
- // The concatenation of an empty string (+'') converts its argument to a string's primitive.
- if (a.constructor === String) return a+'' === b+''; // a+'' - in case 'a' is a String object
- if (b.constructor === String) return b+'' === a+''; // b+'' - in case 'b' is a String object
- return false;
- }
-
- /**
- * Splits the string into an array of values, transforming each value. An empty array is returned for nulls or empty
- * strings
- * @param string
- * @param separator
- */
- function splitVal(string, separator, transform) {
- var val, i, l;
- if (string === null || string.length < 1) return [];
- val = string.split(separator);
- for (i = 0, l = val.length; i < l; i = i + 1) val[i] = transform(val[i]);
- return val;
- }
-
- function getSideBorderPadding(element) {
- return element.outerWidth(false) - element.width();
- }
-
- function installKeyUpChangeEvent(element) {
- var key="keyup-change-value";
- element.on("keydown", function () {
- if ($.data(element, key) === undefined) {
- $.data(element, key, element.val());
- }
- });
- element.on("keyup", function () {
- var val= $.data(element, key);
- if (val !== undefined && element.val() !== val) {
- $.removeData(element, key);
- element.trigger("keyup-change");
- }
- });
- }
-
-
- /**
- * filters mouse events so an event is fired only if the mouse moved.
- *
- * filters out mouse events that occur when mouse is stationary but
- * the elements under the pointer are scrolled.
- */
- function installFilteredMouseMove(element) {
- element.on("mousemove", function (e) {
- var lastpos = lastMousePosition;
- if (lastpos === undefined || lastpos.x !== e.pageX || lastpos.y !== e.pageY) {
- $(e.target).trigger("mousemove-filtered", e);
- }
- });
- }
-
- /**
- * Debounces a function. Returns a function that calls the original fn function only if no invocations have been made
- * within the last quietMillis milliseconds.
- *
- * @param quietMillis number of milliseconds to wait before invoking fn
- * @param fn function to be debounced
- * @param ctx object to be used as this reference within fn
- * @return debounced version of fn
- */
- function debounce(quietMillis, fn, ctx) {
- ctx = ctx || undefined;
- var timeout;
- return function () {
- var args = arguments;
- window.clearTimeout(timeout);
- timeout = window.setTimeout(function() {
- fn.apply(ctx, args);
- }, quietMillis);
- };
- }
-
- function installDebouncedScroll(threshold, element) {
- var notify = debounce(threshold, function (e) { element.trigger("scroll-debounced", e);});
- element.on("scroll", function (e) {
- if (indexOf(e.target, element.get()) >= 0) notify(e);
- });
- }
-
- function focus($el) {
- if ($el[0] === document.activeElement) return;
-
- /* set the focus in a 0 timeout - that way the focus is set after the processing
- of the current event has finished - which seems like the only reliable way
- to set focus */
- window.setTimeout(function() {
- var el=$el[0], pos=$el.val().length, range;
-
- $el.focus();
-
- /* make sure el received focus so we do not error out when trying to manipulate the caret.
- sometimes modals or others listeners may steal it after its set */
- var isVisible = (el.offsetWidth > 0 || el.offsetHeight > 0);
- if (isVisible && el === document.activeElement) {
-
- /* after the focus is set move the caret to the end, necessary when we val()
- just before setting focus */
- if(el.setSelectionRange)
- {
- el.setSelectionRange(pos, pos);
- }
- else if (el.createTextRange) {
- range = el.createTextRange();
- range.collapse(false);
- range.select();
- }
- }
- }, 0);
- }
-
- function getCursorInfo(el) {
- el = $(el)[0];
- var offset = 0;
- var length = 0;
- if ('selectionStart' in el) {
- offset = el.selectionStart;
- length = el.selectionEnd - offset;
- } else if ('selection' in document) {
- el.focus();
- var sel = document.selection.createRange();
- length = document.selection.createRange().text.length;
- sel.moveStart('character', -el.value.length);
- offset = sel.text.length - length;
- }
- return { offset: offset, length: length };
- }
-
- function killEvent(event) {
- event.preventDefault();
- event.stopPropagation();
- }
- function killEventImmediately(event) {
- event.preventDefault();
- event.stopImmediatePropagation();
- }
-
- function measureTextWidth(e) {
- if (!sizer){
- var style = e[0].currentStyle || window.getComputedStyle(e[0], null);
- sizer = $(document.createElement("div")).css({
- position: "absolute",
- left: "-10000px",
- top: "-10000px",
- display: "none",
- fontSize: style.fontSize,
- fontFamily: style.fontFamily,
- fontStyle: style.fontStyle,
- fontWeight: style.fontWeight,
- letterSpacing: style.letterSpacing,
- textTransform: style.textTransform,
- whiteSpace: "nowrap"
- });
- sizer.attr("class","select2-sizer");
- $(document.body).append(sizer);
- }
- sizer.text(e.val());
- return sizer.width();
- }
-
- function syncCssClasses(dest, src, adapter) {
- var classes, replacements = [], adapted;
-
- classes = $.trim(dest.attr("class"));
-
- if (classes) {
- classes = '' + classes; // for IE which returns object
-
- $(classes.split(/\s+/)).each2(function() {
- if (this.indexOf("select2-") === 0) {
- replacements.push(this);
- }
- });
- }
-
- classes = $.trim(src.attr("class"));
-
- if (classes) {
- classes = '' + classes; // for IE which returns object
-
- $(classes.split(/\s+/)).each2(function() {
- if (this.indexOf("select2-") !== 0) {
- adapted = adapter(this);
-
- if (adapted) {
- replacements.push(adapted);
- }
- }
- });
- }
-
- dest.attr("class", replacements.join(" "));
- }
-
-
- function markMatch(text, term, markup, escapeMarkup) {
- var match=stripDiacritics(text.toUpperCase()).indexOf(stripDiacritics(term.toUpperCase())),
- tl=term.length;
-
- if (match<0) {
- markup.push(escapeMarkup(text));
- return;
- }
-
- markup.push(escapeMarkup(text.substring(0, match)));
- markup.push("
");
- markup.push(escapeMarkup(text.substring(match, match + tl)));
- markup.push(" ");
- markup.push(escapeMarkup(text.substring(match + tl, text.length)));
- }
-
- function defaultEscapeMarkup(markup) {
- var replace_map = {
- '\\': '\',
- '&': '&',
- '<': '<',
- '>': '>',
- '"': '"',
- "'": ''',
- "/": '/'
- };
-
- return String(markup).replace(/[&<>"'\/\\]/g, function (match) {
- return replace_map[match];
- });
- }
-
- /**
- * Produces an ajax-based query function
- *
- * @param options object containing configuration parameters
- * @param options.params parameter map for the transport ajax call, can contain such options as cache, jsonpCallback, etc. see $.ajax
- * @param options.transport function that will be used to execute the ajax request. must be compatible with parameters supported by $.ajax
- * @param options.url url for the data
- * @param options.data a function(searchTerm, pageNumber, context) that should return an object containing query string parameters for the above url.
- * @param options.dataType request data type: ajax, jsonp, other datatypes supported by jQuery's $.ajax function or the transport function if specified
- * @param options.quietMillis (optional) milliseconds to wait before making the ajaxRequest, helps debounce the ajax function if invoked too often
- * @param options.results a function(remoteData, pageNumber, query) that converts data returned form the remote request to the format expected by Select2.
- * The expected format is an object containing the following keys:
- * results array of objects that will be used as choices
- * more (optional) boolean indicating whether there are more results available
- * Example: {results:[{id:1, text:'Red'},{id:2, text:'Blue'}], more:true}
- */
- function ajax(options) {
- var timeout, // current scheduled but not yet executed request
- handler = null,
- quietMillis = options.quietMillis || 100,
- ajaxUrl = options.url,
- self = this;
-
- return function (query) {
- window.clearTimeout(timeout);
- timeout = window.setTimeout(function () {
- var data = options.data, // ajax data function
- url = ajaxUrl, // ajax url string or function
- transport = options.transport || $.fn.select2.ajaxDefaults.transport,
- // deprecated - to be removed in 4.0 - use params instead
- deprecated = {
- type: options.type || 'GET', // set type of request (GET or POST)
- cache: options.cache || false,
- jsonpCallback: options.jsonpCallback||undefined,
- dataType: options.dataType||"json"
- },
- params = $.extend({}, $.fn.select2.ajaxDefaults.params, deprecated);
-
- data = data ? data.call(self, query.term, query.page, query.context) : null;
- url = (typeof url === 'function') ? url.call(self, query.term, query.page, query.context) : url;
-
- if (handler && typeof handler.abort === "function") { handler.abort(); }
-
- if (options.params) {
- if ($.isFunction(options.params)) {
- $.extend(params, options.params.call(self));
- } else {
- $.extend(params, options.params);
- }
- }
-
- $.extend(params, {
- url: url,
- dataType: options.dataType,
- data: data,
- success: function (data) {
- // TODO - replace query.page with query so users have access to term, page, etc.
- // added query as third paramter to keep backwards compatibility
- var results = options.results(data, query.page, query);
- query.callback(results);
- },
- error: function(jqXHR, textStatus, errorThrown){
- var results = {
- hasError: true,
- jqXHR: jqXHR,
- textStatus: textStatus,
- errorThrown: errorThrown
- };
-
- query.callback(results);
- }
- });
- handler = transport.call(self, params);
- }, quietMillis);
- };
- }
-
- /**
- * Produces a query function that works with a local array
- *
- * @param options object containing configuration parameters. The options parameter can either be an array or an
- * object.
- *
- * If the array form is used it is assumed that it contains objects with 'id' and 'text' keys.
- *
- * If the object form is used it is assumed that it contains 'data' and 'text' keys. The 'data' key should contain
- * an array of objects that will be used as choices. These objects must contain at least an 'id' key. The 'text'
- * key can either be a String in which case it is expected that each element in the 'data' array has a key with the
- * value of 'text' which will be used to match choices. Alternatively, text can be a function(item) that can extract
- * the text.
- */
- function local(options) {
- var data = options, // data elements
- dataText,
- tmp,
- text = function (item) { return ""+item.text; }; // function used to retrieve the text portion of a data item that is matched against the search
-
- if ($.isArray(data)) {
- tmp = data;
- data = { results: tmp };
- }
-
- if ($.isFunction(data) === false) {
- tmp = data;
- data = function() { return tmp; };
- }
-
- var dataItem = data();
- if (dataItem.text) {
- text = dataItem.text;
- // if text is not a function we assume it to be a key name
- if (!$.isFunction(text)) {
- dataText = dataItem.text; // we need to store this in a separate variable because in the next step data gets reset and data.text is no longer available
- text = function (item) { return item[dataText]; };
- }
- }
-
- return function (query) {
- var t = query.term, filtered = { results: [] }, process;
- if (t === "") {
- query.callback(data());
- return;
- }
-
- process = function(datum, collection) {
- var group, attr;
- datum = datum[0];
- if (datum.children) {
- group = {};
- for (attr in datum) {
- if (datum.hasOwnProperty(attr)) group[attr]=datum[attr];
- }
- group.children=[];
- $(datum.children).each2(function(i, childDatum) { process(childDatum, group.children); });
- if (group.children.length || query.matcher(t, text(group), datum)) {
- collection.push(group);
- }
- } else {
- if (query.matcher(t, text(datum), datum)) {
- collection.push(datum);
- }
- }
- };
-
- $(data().results).each2(function(i, datum) { process(datum, filtered.results); });
- query.callback(filtered);
- };
- }
-
- // TODO javadoc
- function tags(data) {
- var isFunc = $.isFunction(data);
- return function (query) {
- var t = query.term, filtered = {results: []};
- var result = isFunc ? data(query) : data;
- if ($.isArray(result)) {
- $(result).each(function () {
- var isObject = this.text !== undefined,
- text = isObject ? this.text : this;
- if (t === "" || query.matcher(t, text)) {
- filtered.results.push(isObject ? this : {id: this, text: this});
- }
- });
- query.callback(filtered);
- }
- };
- }
-
- /**
- * Checks if the formatter function should be used.
- *
- * Throws an error if it is not a function. Returns true if it should be used,
- * false if no formatting should be performed.
- *
- * @param formatter
- */
- function checkFormatter(formatter, formatterName) {
- if ($.isFunction(formatter)) return true;
- if (!formatter) return false;
- if (typeof(formatter) === 'string') return true;
- throw new Error(formatterName +" must be a string, function, or falsy value");
- }
-
- /**
- * Returns a given value
- * If given a function, returns its output
- *
- * @param val string|function
- * @param context value of "this" to be passed to function
- * @returns {*}
- */
- function evaluate(val, context) {
- if ($.isFunction(val)) {
- var args = Array.prototype.slice.call(arguments, 2);
- return val.apply(context, args);
- }
- return val;
- }
-
- function countResults(results) {
- var count = 0;
- $.each(results, function(i, item) {
- if (item.children) {
- count += countResults(item.children);
- } else {
- count++;
- }
- });
- return count;
- }
-
- /**
- * Default tokenizer. This function uses breaks the input on substring match of any string from the
- * opts.tokenSeparators array and uses opts.createSearchChoice to create the choice object. Both of those
- * two options have to be defined in order for the tokenizer to work.
- *
- * @param input text user has typed so far or pasted into the search field
- * @param selection currently selected choices
- * @param selectCallback function(choice) callback tho add the choice to selection
- * @param opts select2's opts
- * @return undefined/null to leave the current input unchanged, or a string to change the input to the returned value
- */
- function defaultTokenizer(input, selection, selectCallback, opts) {
- var original = input, // store the original so we can compare and know if we need to tell the search to update its text
- dupe = false, // check for whether a token we extracted represents a duplicate selected choice
- token, // token
- index, // position at which the separator was found
- i, l, // looping variables
- separator; // the matched separator
-
- if (!opts.createSearchChoice || !opts.tokenSeparators || opts.tokenSeparators.length < 1) return undefined;
-
- while (true) {
- index = -1;
-
- for (i = 0, l = opts.tokenSeparators.length; i < l; i++) {
- separator = opts.tokenSeparators[i];
- index = input.indexOf(separator);
- if (index >= 0) break;
- }
-
- if (index < 0) break; // did not find any token separator in the input string, bail
-
- token = input.substring(0, index);
- input = input.substring(index + separator.length);
-
- if (token.length > 0) {
- token = opts.createSearchChoice.call(this, token, selection);
- if (token !== undefined && token !== null && opts.id(token) !== undefined && opts.id(token) !== null) {
- dupe = false;
- for (i = 0, l = selection.length; i < l; i++) {
- if (equal(opts.id(token), opts.id(selection[i]))) {
- dupe = true; break;
- }
- }
-
- if (!dupe) selectCallback(token);
- }
- }
- }
-
- if (original!==input) return input;
- }
-
- function cleanupJQueryElements() {
- var self = this;
-
- $.each(arguments, function (i, element) {
- self[element].remove();
- self[element] = null;
- });
- }
-
- /**
- * Creates a new class
- *
- * @param superClass
- * @param methods
- */
- function clazz(SuperClass, methods) {
- var constructor = function () {};
- constructor.prototype = new SuperClass;
- constructor.prototype.constructor = constructor;
- constructor.prototype.parent = SuperClass.prototype;
- constructor.prototype = $.extend(constructor.prototype, methods);
- return constructor;
- }
-
- AbstractSelect2 = clazz(Object, {
-
- // abstract
- bind: function (func) {
- var self = this;
- return function () {
- func.apply(self, arguments);
- };
- },
-
- // abstract
- init: function (opts) {
- var results, search, resultsSelector = ".select2-results";
-
- // prepare options
- this.opts = opts = this.prepareOpts(opts);
-
- this.id=opts.id;
-
- // destroy if called on an existing component
- if (opts.element.data("select2") !== undefined &&
- opts.element.data("select2") !== null) {
- opts.element.data("select2").destroy();
- }
-
- this.container = this.createContainer();
-
- this.liveRegion = $('.select2-hidden-accessible');
- if (this.liveRegion.length == 0) {
- this.liveRegion = $("
", {
- role: "status",
- "aria-live": "polite"
- })
- .addClass("select2-hidden-accessible")
- .appendTo(document.body);
- }
-
- this.containerId="s2id_"+(opts.element.attr("id") || "autogen"+nextUid());
- this.containerEventName= this.containerId
- .replace(/([.])/g, '_')
- .replace(/([;&,\-\.\+\*\~':"\!\^#$%@\[\]\(\)=>\|])/g, '\\$1');
- this.container.attr("id", this.containerId);
-
- this.container.attr("title", opts.element.attr("title"));
-
- this.body = $(document.body);
-
- syncCssClasses(this.container, this.opts.element, this.opts.adaptContainerCssClass);
-
- this.container.attr("style", opts.element.attr("style"));
- this.container.css(evaluate(opts.containerCss, this.opts.element));
- this.container.addClass(evaluate(opts.containerCssClass, this.opts.element));
-
- this.elementTabIndex = this.opts.element.attr("tabindex");
-
- // swap container for the element
- this.opts.element
- .data("select2", this)
- .attr("tabindex", "-1")
- .before(this.container)
- .on("click.select2", killEvent); // do not leak click events
-
- this.container.data("select2", this);
-
- this.dropdown = this.container.find(".select2-drop");
-
- syncCssClasses(this.dropdown, this.opts.element, this.opts.adaptDropdownCssClass);
-
- this.dropdown.addClass(evaluate(opts.dropdownCssClass, this.opts.element));
- this.dropdown.data("select2", this);
- this.dropdown.on("click", killEvent);
-
- this.results = results = this.container.find(resultsSelector);
- this.search = search = this.container.find("input.select2-input");
-
- this.queryCount = 0;
- this.resultsPage = 0;
- this.context = null;
-
- // initialize the container
- this.initContainer();
-
- this.container.on("click", killEvent);
-
- installFilteredMouseMove(this.results);
-
- this.dropdown.on("mousemove-filtered", resultsSelector, this.bind(this.highlightUnderEvent));
- this.dropdown.on("touchstart touchmove touchend", resultsSelector, this.bind(function (event) {
- this._touchEvent = true;
- this.highlightUnderEvent(event);
- }));
- this.dropdown.on("touchmove", resultsSelector, this.bind(this.touchMoved));
- this.dropdown.on("touchstart touchend", resultsSelector, this.bind(this.clearTouchMoved));
-
- // Waiting for a click event on touch devices to select option and hide dropdown
- // otherwise click will be triggered on an underlying element
- this.dropdown.on('click', this.bind(function (event) {
- if (this._touchEvent) {
- this._touchEvent = false;
- this.selectHighlighted();
- }
- }));
-
- installDebouncedScroll(80, this.results);
- this.dropdown.on("scroll-debounced", resultsSelector, this.bind(this.loadMoreIfNeeded));
-
- // do not propagate change event from the search field out of the component
- $(this.container).on("change", ".select2-input", function(e) {e.stopPropagation();});
- $(this.dropdown).on("change", ".select2-input", function(e) {e.stopPropagation();});
-
- // if jquery.mousewheel plugin is installed we can prevent out-of-bounds scrolling of results via mousewheel
- if ($.fn.mousewheel) {
- results.mousewheel(function (e, delta, deltaX, deltaY) {
- var top = results.scrollTop();
- if (deltaY > 0 && top - deltaY <= 0) {
- results.scrollTop(0);
- killEvent(e);
- } else if (deltaY < 0 && results.get(0).scrollHeight - results.scrollTop() + deltaY <= results.height()) {
- results.scrollTop(results.get(0).scrollHeight - results.height());
- killEvent(e);
- }
- });
- }
-
- installKeyUpChangeEvent(search);
- search.on("keyup-change input paste", this.bind(this.updateResults));
- search.on("focus", function () { search.addClass("select2-focused"); });
- search.on("blur", function () { search.removeClass("select2-focused");});
-
- this.dropdown.on("mouseup", resultsSelector, this.bind(function (e) {
- if ($(e.target).closest(".select2-result-selectable").length > 0) {
- this.highlightUnderEvent(e);
- this.selectHighlighted(e);
- }
- }));
-
- // trap all mouse events from leaving the dropdown. sometimes there may be a modal that is listening
- // for mouse events outside of itself so it can close itself. since the dropdown is now outside the select2's
- // dom it will trigger the popup close, which is not what we want
- // focusin can cause focus wars between modals and select2 since the dropdown is outside the modal.
- this.dropdown.on("click mouseup mousedown touchstart touchend focusin", function (e) { e.stopPropagation(); });
-
- this.lastSearchTerm = undefined;
-
- if ($.isFunction(this.opts.initSelection)) {
- // initialize selection based on the current value of the source element
- this.initSelection();
-
- // if the user has provided a function that can set selection based on the value of the source element
- // we monitor the change event on the element and trigger it, allowing for two way synchronization
- this.monitorSource();
- }
-
- if (opts.maximumInputLength !== null) {
- this.search.attr("maxlength", opts.maximumInputLength);
- }
-
- var disabled = opts.element.prop("disabled");
- if (disabled === undefined) disabled = false;
- this.enable(!disabled);
-
- var readonly = opts.element.prop("readonly");
- if (readonly === undefined) readonly = false;
- this.readonly(readonly);
-
- // Calculate size of scrollbar
- scrollBarDimensions = scrollBarDimensions || measureScrollbar();
-
- this.autofocus = opts.element.prop("autofocus");
- opts.element.prop("autofocus", false);
- if (this.autofocus) this.focus();
-
- this.search.attr("placeholder", opts.searchInputPlaceholder);
- },
-
- // abstract
- destroy: function () {
- var element=this.opts.element, select2 = element.data("select2"), self = this;
-
- this.close();
-
- if (element.length && element[0].detachEvent && self._sync) {
- element.each(function () {
- if (self._sync) {
- this.detachEvent("onpropertychange", self._sync);
- }
- });
- }
- if (this.propertyObserver) {
- this.propertyObserver.disconnect();
- this.propertyObserver = null;
- }
- this._sync = null;
-
- if (select2 !== undefined) {
- select2.container.remove();
- select2.liveRegion.remove();
- select2.dropdown.remove();
- element.removeData("select2")
- .off(".select2");
- if (!element.is("input[type='hidden']")) {
- element
- .show()
- .prop("autofocus", this.autofocus || false);
- if (this.elementTabIndex) {
- element.attr({tabindex: this.elementTabIndex});
- } else {
- element.removeAttr("tabindex");
- }
- element.show();
- } else {
- element.css("display", "");
- }
- }
-
- cleanupJQueryElements.call(this,
- "container",
- "liveRegion",
- "dropdown",
- "results",
- "search"
- );
- },
-
- // abstract
- optionToData: function(element) {
- if (element.is("option")) {
- return {
- id:element.prop("value"),
- text:element.text(),
- element: element.get(),
- css: element.attr("class"),
- disabled: element.prop("disabled"),
- locked: equal(element.attr("locked"), "locked") || equal(element.data("locked"), true)
- };
- } else if (element.is("optgroup")) {
- return {
- text:element.attr("label"),
- children:[],
- element: element.get(),
- css: element.attr("class")
- };
- }
- },
-
- // abstract
- prepareOpts: function (opts) {
- var element, select, idKey, ajaxUrl, self = this;
-
- element = opts.element;
-
- if (element.get(0).tagName.toLowerCase() === "select") {
- this.select = select = opts.element;
- }
-
- if (select) {
- // these options are not allowed when attached to a select because they are picked up off the element itself
- $.each(["id", "multiple", "ajax", "query", "createSearchChoice", "initSelection", "data", "tags"], function () {
- if (this in opts) {
- throw new Error("Option '" + this + "' is not allowed for Select2 when attached to a element.");
- }
- });
- }
-
- opts.debug = opts.debug || $.fn.select2.defaults.debug;
-
- // Warnings for options renamed/removed in Select2 4.0.0
- // Only when it's enabled through debug mode
- if (opts.debug && console && console.warn) {
- // id was removed
- if (opts.id != null) {
- console.warn(
- 'Select2: The `id` option has been removed in Select2 4.0.0, ' +
- 'consider renaming your `id` property or mapping the property before your data makes it to Select2. ' +
- 'You can read more at https://select2.github.io/announcements-4.0.html#changed-id'
- );
- }
-
- // text was removed
- if (opts.text != null) {
- console.warn(
- 'Select2: The `text` option has been removed in Select2 4.0.0, ' +
- 'consider renaming your `text` property or mapping the property before your data makes it to Select2. ' +
- 'You can read more at https://select2.github.io/announcements-4.0.html#changed-id'
- );
- }
-
- // sortResults was renamed to results
- if (opts.sortResults != null) {
- console.warn(
- 'Select2: the `sortResults` option has been renamed to `sorter` in Select2 4.0.0. '
- );
- }
-
- // selectOnBlur was renamed to selectOnClose
- if (opts.selectOnBlur != null) {
- console.warn(
- 'Select2: The `selectOnBlur` option has been renamed to `selectOnClose` in Select2 4.0.0.'
- );
- }
-
- // ajax.results was renamed to ajax.processResults
- if (opts.ajax != null && opts.ajax.results != null) {
- console.warn(
- 'Select2: The `ajax.results` option has been renamed to `ajax.processResults` in Select2 4.0.0.'
- );
- }
-
- // format* options were renamed to language.*
- if (opts.formatNoResults != null) {
- console.warn(
- 'Select2: The `formatNoResults` option has been renamed to `language.noResults` in Select2 4.0.0.'
- );
- }
- if (opts.formatSearching != null) {
- console.warn(
- 'Select2: The `formatSearching` option has been renamed to `language.searching` in Select2 4.0.0.'
- );
- }
- if (opts.formatInputTooShort != null) {
- console.warn(
- 'Select2: The `formatInputTooShort` option has been renamed to `language.inputTooShort` in Select2 4.0.0.'
- );
- }
- if (opts.formatInputTooLong != null) {
- console.warn(
- 'Select2: The `formatInputTooLong` option has been renamed to `language.inputTooLong` in Select2 4.0.0.'
- );
- }
- if (opts.formatLoading != null) {
- console.warn(
- 'Select2: The `formatLoading` option has been renamed to `language.loadingMore` in Select2 4.0.0.'
- );
- }
- if (opts.formatSelectionTooBig != null) {
- console.warn(
- 'Select2: The `formatSelectionTooBig` option has been renamed to `language.maximumSelected` in Select2 4.0.0.'
- );
- }
-
- if (opts.element.data('select2Tags')) {
- console.warn(
- 'Select2: The `data-select2-tags` attribute has been renamed to `data-tags` in Select2 4.0.0.'
- );
- }
- }
-
- // Aliasing options renamed in Select2 4.0.0
-
- // data-select2-tags -> data-tags
- if (opts.element.data('tags') != null) {
- var elemTags = opts.element.data('tags');
-
- // data-tags should actually be a boolean
- if (!$.isArray(elemTags)) {
- elemTags = [];
- }
-
- opts.element.data('select2Tags', elemTags);
- }
-
- // sortResults -> sorter
- if (opts.sorter != null) {
- opts.sortResults = opts.sorter;
- }
-
- // selectOnBlur -> selectOnClose
- if (opts.selectOnClose != null) {
- opts.selectOnBlur = opts.selectOnClose;
- }
-
- // ajax.results -> ajax.processResults
- if (opts.ajax != null) {
- if ($.isFunction(opts.ajax.processResults)) {
- opts.ajax.results = opts.ajax.processResults;
- }
- }
-
- // Formatters/language options
- if (opts.language != null) {
- var lang = opts.language;
-
- // formatNoMatches -> language.noMatches
- if ($.isFunction(lang.noMatches)) {
- opts.formatNoMatches = lang.noMatches;
- }
-
- // formatSearching -> language.searching
- if ($.isFunction(lang.searching)) {
- opts.formatSearching = lang.searching;
- }
-
- // formatInputTooShort -> language.inputTooShort
- if ($.isFunction(lang.inputTooShort)) {
- opts.formatInputTooShort = lang.inputTooShort;
- }
-
- // formatInputTooLong -> language.inputTooLong
- if ($.isFunction(lang.inputTooLong)) {
- opts.formatInputTooLong = lang.inputTooLong;
- }
-
- // formatLoading -> language.loadingMore
- if ($.isFunction(lang.loadingMore)) {
- opts.formatLoading = lang.loadingMore;
- }
-
- // formatSelectionTooBig -> language.maximumSelected
- if ($.isFunction(lang.maximumSelected)) {
- opts.formatSelectionTooBig = lang.maximumSelected;
- }
- }
-
- opts = $.extend({}, {
- populateResults: function(container, results, query) {
- var populate, id=this.opts.id, liveRegion=this.liveRegion;
-
- populate=function(results, container, depth) {
-
- var i, l, result, selectable, disabled, compound, node, label, innerContainer, formatted;
-
- results = opts.sortResults(results, container, query);
-
- // collect the created nodes for bulk append
- var nodes = [];
- for (i = 0, l = results.length; i < l; i = i + 1) {
-
- result=results[i];
-
- disabled = (result.disabled === true);
- selectable = (!disabled) && (id(result) !== undefined);
-
- compound=result.children && result.children.length > 0;
-
- node=$(" ");
- node.addClass("select2-results-dept-"+depth);
- node.addClass("select2-result");
- node.addClass(selectable ? "select2-result-selectable" : "select2-result-unselectable");
- if (disabled) { node.addClass("select2-disabled"); }
- if (compound) { node.addClass("select2-result-with-children"); }
- node.addClass(self.opts.formatResultCssClass(result));
- node.attr("role", "presentation");
-
- label=$(document.createElement("div"));
- label.addClass("select2-result-label");
- label.attr("id", "select2-result-label-" + nextUid());
- label.attr("role", "option");
-
- formatted=opts.formatResult(result, label, query, self.opts.escapeMarkup);
- if (formatted!==undefined) {
- label.html(formatted);
- node.append(label);
- }
-
-
- if (compound) {
- innerContainer=$("");
- innerContainer.addClass("select2-result-sub");
- populate(result.children, innerContainer, depth+1);
- node.append(innerContainer);
- }
-
- node.data("select2-data", result);
- nodes.push(node[0]);
- }
-
- // bulk append the created nodes
- container.append(nodes);
- liveRegion.text(opts.formatMatches(results.length));
- };
-
- populate(results, container, 0);
- }
- }, $.fn.select2.defaults, opts);
-
- if (typeof(opts.id) !== "function") {
- idKey = opts.id;
- opts.id = function (e) { return e[idKey]; };
- }
-
- if ($.isArray(opts.element.data("select2Tags"))) {
- if ("tags" in opts) {
- throw "tags specified as both an attribute 'data-select2-tags' and in options of Select2 " + opts.element.attr("id");
- }
- opts.tags=opts.element.data("select2Tags");
- }
-
- if (select) {
- opts.query = this.bind(function (query) {
- var data = { results: [], more: false },
- term = query.term,
- children, placeholderOption, process;
-
- process=function(element, collection) {
- var group;
- if (element.is("option")) {
- if (query.matcher(term, element.text(), element)) {
- collection.push(self.optionToData(element));
- }
- } else if (element.is("optgroup")) {
- group=self.optionToData(element);
- element.children().each2(function(i, elm) { process(elm, group.children); });
- if (group.children.length>0) {
- collection.push(group);
- }
- }
- };
-
- children=element.children();
-
- // ignore the placeholder option if there is one
- if (this.getPlaceholder() !== undefined && children.length > 0) {
- placeholderOption = this.getPlaceholderOption();
- if (placeholderOption) {
- children=children.not(placeholderOption);
- }
- }
-
- children.each2(function(i, elm) { process(elm, data.results); });
-
- query.callback(data);
- });
- // this is needed because inside val() we construct choices from options and their id is hardcoded
- opts.id=function(e) { return e.id; };
- } else {
- if (!("query" in opts)) {
- if ("ajax" in opts) {
- ajaxUrl = opts.element.data("ajax-url");
- if (ajaxUrl && ajaxUrl.length > 0) {
- opts.ajax.url = ajaxUrl;
- }
- opts.query = ajax.call(opts.element, opts.ajax);
- } else if ("data" in opts) {
- opts.query = local(opts.data);
- } else if ("tags" in opts) {
- opts.query = tags(opts.tags);
- if (opts.createSearchChoice === undefined) {
- opts.createSearchChoice = function (term) { return {id: $.trim(term), text: $.trim(term)}; };
- }
- if (opts.initSelection === undefined) {
- opts.initSelection = function (element, callback) {
- var data = [];
- $(splitVal(element.val(), opts.separator, opts.transformVal)).each(function () {
- var obj = { id: this, text: this },
- tags = opts.tags;
- if ($.isFunction(tags)) tags=tags();
- $(tags).each(function() { if (equal(this.id, obj.id)) { obj = this; return false; } });
- data.push(obj);
- });
-
- callback(data);
- };
- }
- }
- }
- }
- if (typeof(opts.query) !== "function") {
- throw "query function not defined for Select2 " + opts.element.attr("id");
- }
-
- if (opts.createSearchChoicePosition === 'top') {
- opts.createSearchChoicePosition = function(list, item) { list.unshift(item); };
- }
- else if (opts.createSearchChoicePosition === 'bottom') {
- opts.createSearchChoicePosition = function(list, item) { list.push(item); };
- }
- else if (typeof(opts.createSearchChoicePosition) !== "function") {
- throw "invalid createSearchChoicePosition option must be 'top', 'bottom' or a custom function";
- }
-
- return opts;
- },
-
- /**
- * Monitor the original element for changes and update select2 accordingly
- */
- // abstract
- monitorSource: function () {
- var el = this.opts.element, observer, self = this;
-
- el.on("change.select2", this.bind(function (e) {
- if (this.opts.element.data("select2-change-triggered") !== true) {
- this.initSelection();
- }
- }));
-
- this._sync = this.bind(function () {
-
- // sync enabled state
- var disabled = el.prop("disabled");
- if (disabled === undefined) disabled = false;
- this.enable(!disabled);
-
- var readonly = el.prop("readonly");
- if (readonly === undefined) readonly = false;
- this.readonly(readonly);
-
- if (this.container) {
- syncCssClasses(this.container, this.opts.element, this.opts.adaptContainerCssClass);
- this.container.addClass(evaluate(this.opts.containerCssClass, this.opts.element));
- }
-
- if (this.dropdown) {
- syncCssClasses(this.dropdown, this.opts.element, this.opts.adaptDropdownCssClass);
- this.dropdown.addClass(evaluate(this.opts.dropdownCssClass, this.opts.element));
- }
-
- });
-
- // IE8-10 (IE9/10 won't fire propertyChange via attachEventListener)
- if (el.length && el[0].attachEvent) {
- el.each(function() {
- this.attachEvent("onpropertychange", self._sync);
- });
- }
-
- // safari, chrome, firefox, IE11
- observer = window.MutationObserver || window.WebKitMutationObserver|| window.MozMutationObserver;
- if (observer !== undefined) {
- if (this.propertyObserver) { delete this.propertyObserver; this.propertyObserver = null; }
- this.propertyObserver = new observer(function (mutations) {
- $.each(mutations, self._sync);
- });
- this.propertyObserver.observe(el.get(0), { attributes:true, subtree:false });
- }
- },
-
- // abstract
- triggerSelect: function(data) {
- var evt = $.Event("select2-selecting", { val: this.id(data), object: data, choice: data });
- this.opts.element.trigger(evt);
- return !evt.isDefaultPrevented();
- },
-
- /**
- * Triggers the change event on the source element
- */
- // abstract
- triggerChange: function (details) {
-
- details = details || {};
- details= $.extend({}, details, { type: "change", val: this.val() });
- // prevents recursive triggering
- this.opts.element.data("select2-change-triggered", true);
- this.opts.element.trigger(details);
- this.opts.element.data("select2-change-triggered", false);
-
- // some validation frameworks ignore the change event and listen instead to keyup, click for selects
- // so here we trigger the click event manually
- this.opts.element.click();
-
- // ValidationEngine ignores the change event and listens instead to blur
- // so here we trigger the blur event manually if so desired
- if (this.opts.blurOnChange)
- this.opts.element.blur();
- },
-
- //abstract
- isInterfaceEnabled: function()
- {
- return this.enabledInterface === true;
- },
-
- // abstract
- enableInterface: function() {
- var enabled = this._enabled && !this._readonly,
- disabled = !enabled;
-
- if (enabled === this.enabledInterface) return false;
-
- this.container.toggleClass("select2-container-disabled", disabled);
- this.close();
- this.enabledInterface = enabled;
-
- return true;
- },
-
- // abstract
- enable: function(enabled) {
- if (enabled === undefined) enabled = true;
- if (this._enabled === enabled) return;
- this._enabled = enabled;
-
- this.opts.element.prop("disabled", !enabled);
- this.enableInterface();
- },
-
- // abstract
- disable: function() {
- this.enable(false);
- },
-
- // abstract
- readonly: function(enabled) {
- if (enabled === undefined) enabled = false;
- if (this._readonly === enabled) return;
- this._readonly = enabled;
-
- this.opts.element.prop("readonly", enabled);
- this.enableInterface();
- },
-
- // abstract
- opened: function () {
- return (this.container) ? this.container.hasClass("select2-dropdown-open") : false;
- },
-
- // abstract
- positionDropdown: function() {
- var $dropdown = this.dropdown,
- container = this.container,
- offset = container.offset(),
- height = container.outerHeight(false),
- width = container.outerWidth(false),
- dropHeight = $dropdown.outerHeight(false),
- $window = $(window),
- windowWidth = $window.width(),
- windowHeight = $window.height(),
- viewPortRight = $window.scrollLeft() + windowWidth,
- viewportBottom = $window.scrollTop() + windowHeight,
- dropTop = offset.top + height,
- dropLeft = offset.left,
- enoughRoomBelow = dropTop + dropHeight <= viewportBottom,
- enoughRoomAbove = (offset.top - dropHeight) >= $window.scrollTop(),
- dropWidth = $dropdown.outerWidth(false),
- enoughRoomOnRight = function() {
- return dropLeft + dropWidth <= viewPortRight;
- },
- enoughRoomOnLeft = function() {
- return offset.left + viewPortRight + container.outerWidth(false) > dropWidth;
- },
- aboveNow = $dropdown.hasClass("select2-drop-above"),
- bodyOffset,
- above,
- changeDirection,
- css,
- resultsListNode;
-
- // always prefer the current above/below alignment, unless there is not enough room
- if (aboveNow) {
- above = true;
- if (!enoughRoomAbove && enoughRoomBelow) {
- changeDirection = true;
- above = false;
- }
- } else {
- above = false;
- if (!enoughRoomBelow && enoughRoomAbove) {
- changeDirection = true;
- above = true;
- }
- }
-
- //if we are changing direction we need to get positions when dropdown is hidden;
- if (changeDirection) {
- $dropdown.hide();
- offset = this.container.offset();
- height = this.container.outerHeight(false);
- width = this.container.outerWidth(false);
- dropHeight = $dropdown.outerHeight(false);
- viewPortRight = $window.scrollLeft() + windowWidth;
- viewportBottom = $window.scrollTop() + windowHeight;
- dropTop = offset.top + height;
- dropLeft = offset.left;
- dropWidth = $dropdown.outerWidth(false);
- $dropdown.show();
-
- // fix so the cursor does not move to the left within the search-textbox in IE
- this.focusSearch();
- }
-
- if (this.opts.dropdownAutoWidth) {
- resultsListNode = $('.select2-results', $dropdown)[0];
- $dropdown.addClass('select2-drop-auto-width');
- $dropdown.css('width', '');
- // Add scrollbar width to dropdown if vertical scrollbar is present
- dropWidth = $dropdown.outerWidth(false) + (resultsListNode.scrollHeight === resultsListNode.clientHeight ? 0 : scrollBarDimensions.width);
- dropWidth > width ? width = dropWidth : dropWidth = width;
- dropHeight = $dropdown.outerHeight(false);
- }
- else {
- this.container.removeClass('select2-drop-auto-width');
- }
-
- //console.log("below/ droptop:", dropTop, "dropHeight", dropHeight, "sum", (dropTop+dropHeight)+" viewport bottom", viewportBottom, "enough?", enoughRoomBelow);
- //console.log("above/ offset.top", offset.top, "dropHeight", dropHeight, "top", (offset.top-dropHeight), "scrollTop", this.body.scrollTop(), "enough?", enoughRoomAbove);
-
- // fix positioning when body has an offset and is not position: static
- if (this.body.css('position') !== 'static') {
- bodyOffset = this.body.offset();
- dropTop -= bodyOffset.top;
- dropLeft -= bodyOffset.left;
- }
-
- if (!enoughRoomOnRight() && enoughRoomOnLeft()) {
- dropLeft = offset.left + this.container.outerWidth(false) - dropWidth;
- }
-
- css = {
- left: dropLeft,
- width: width
- };
-
- if (above) {
- this.container.addClass("select2-drop-above");
- $dropdown.addClass("select2-drop-above");
- dropHeight = $dropdown.outerHeight(false);
- css.top = offset.top - dropHeight;
- css.bottom = 'auto';
- }
- else {
- css.top = dropTop;
- css.bottom = 'auto';
- this.container.removeClass("select2-drop-above");
- $dropdown.removeClass("select2-drop-above");
- }
- css = $.extend(css, evaluate(this.opts.dropdownCss, this.opts.element));
-
- $dropdown.css(css);
- },
-
- // abstract
- shouldOpen: function() {
- var event;
-
- if (this.opened()) return false;
-
- if (this._enabled === false || this._readonly === true) return false;
-
- event = $.Event("select2-opening");
- this.opts.element.trigger(event);
- return !event.isDefaultPrevented();
- },
-
- // abstract
- clearDropdownAlignmentPreference: function() {
- // clear the classes used to figure out the preference of where the dropdown should be opened
- this.container.removeClass("select2-drop-above");
- this.dropdown.removeClass("select2-drop-above");
- },
-
- /**
- * Opens the dropdown
- *
- * @return {Boolean} whether or not dropdown was opened. This method will return false if, for example,
- * the dropdown is already open, or if the 'open' event listener on the element called preventDefault().
- */
- // abstract
- open: function () {
-
- if (!this.shouldOpen()) return false;
-
- this.opening();
-
- // Only bind the document mousemove when the dropdown is visible
- $document.on("mousemove.select2Event", function (e) {
- lastMousePosition.x = e.pageX;
- lastMousePosition.y = e.pageY;
- });
-
- return true;
- },
-
- /**
- * Performs the opening of the dropdown
- */
- // abstract
- opening: function() {
- var cid = this.containerEventName,
- scroll = "scroll." + cid,
- resize = "resize."+cid,
- orient = "orientationchange."+cid,
- mask;
-
- this.container.addClass("select2-dropdown-open").addClass("select2-container-active");
-
- this.clearDropdownAlignmentPreference();
-
- if(this.dropdown[0] !== this.body.children().last()[0]) {
- this.dropdown.detach().appendTo(this.body);
- }
-
- // create the dropdown mask if doesn't already exist
- mask = $("#select2-drop-mask");
- if (mask.length === 0) {
- mask = $(document.createElement("div"));
- mask.attr("id","select2-drop-mask").attr("class","select2-drop-mask");
- mask.hide();
- mask.appendTo(this.body);
- mask.on("mousedown touchstart click", function (e) {
- // Prevent IE from generating a click event on the body
- reinsertElement(mask);
-
- var dropdown = $("#select2-drop"), self;
- if (dropdown.length > 0) {
- self=dropdown.data("select2");
- if (self.opts.selectOnBlur) {
- self.selectHighlighted({noFocus: true});
- }
- self.close();
- e.preventDefault();
- e.stopPropagation();
- }
- });
- }
-
- // ensure the mask is always right before the dropdown
- if (this.dropdown.prev()[0] !== mask[0]) {
- this.dropdown.before(mask);
- }
-
- // move the global id to the correct dropdown
- $("#select2-drop").removeAttr("id");
- this.dropdown.attr("id", "select2-drop");
-
- // show the elements
- mask.show();
-
- this.positionDropdown();
- this.dropdown.show();
- this.positionDropdown();
-
- this.dropdown.addClass("select2-drop-active");
-
- // attach listeners to events that can change the position of the container and thus require
- // the position of the dropdown to be updated as well so it does not come unglued from the container
- var that = this;
- this.container.parents().add(window).each(function () {
- $(this).on(resize+" "+scroll+" "+orient, function (e) {
- if (that.opened()) that.positionDropdown();
- });
- });
-
-
- },
-
- // abstract
- close: function () {
- if (!this.opened()) return;
-
- var cid = this.containerEventName,
- scroll = "scroll." + cid,
- resize = "resize."+cid,
- orient = "orientationchange."+cid;
-
- // unbind event listeners
- this.container.parents().add(window).each(function () { $(this).off(scroll).off(resize).off(orient); });
-
- this.clearDropdownAlignmentPreference();
-
- $("#select2-drop-mask").hide();
- this.dropdown.removeAttr("id"); // only the active dropdown has the select2-drop id
- this.dropdown.hide();
- this.container.removeClass("select2-dropdown-open").removeClass("select2-container-active");
- this.results.empty();
-
- // Now that the dropdown is closed, unbind the global document mousemove event
- $document.off("mousemove.select2Event");
-
- this.clearSearch();
- this.search.removeClass("select2-active");
-
- // Remove the aria active descendant for highlighted element
- this.search.removeAttr("aria-activedescendant");
- this.opts.element.trigger($.Event("select2-close"));
- },
-
- /**
- * Opens control, sets input value, and updates results.
- */
- // abstract
- externalSearch: function (term) {
- this.open();
- this.search.val(term);
- this.updateResults(false);
- },
-
- // abstract
- clearSearch: function () {
-
- },
-
- /**
- * @return {Boolean} Whether or not search value was changed.
- * @private
- */
- prefillNextSearchTerm: function () {
- // initializes search's value with nextSearchTerm (if defined by user)
- // ignore nextSearchTerm if the dropdown is opened by the user pressing a letter
- if(this.search.val() !== "") {
- return false;
- }
-
- var nextSearchTerm = this.opts.nextSearchTerm(this.data(), this.lastSearchTerm);
- if(nextSearchTerm !== undefined){
- this.search.val(nextSearchTerm);
- this.search.select();
- return true;
- }
-
- return false;
- },
-
- //abstract
- getMaximumSelectionSize: function() {
- return evaluate(this.opts.maximumSelectionSize, this.opts.element);
- },
-
- // abstract
- ensureHighlightVisible: function () {
- var results = this.results, children, index, child, hb, rb, y, more, topOffset;
-
- index = this.highlight();
-
- if (index < 0) return;
-
- if (index == 0) {
-
- // if the first element is highlighted scroll all the way to the top,
- // that way any unselectable headers above it will also be scrolled
- // into view
-
- results.scrollTop(0);
- return;
- }
-
- children = this.findHighlightableChoices().find('.select2-result-label');
-
- child = $(children[index]);
-
- topOffset = (child.offset() || {}).top || 0;
-
- hb = topOffset + child.outerHeight(true);
-
- // if this is the last child lets also make sure select2-more-results is visible
- if (index === children.length - 1) {
- more = results.find("li.select2-more-results");
- if (more.length > 0) {
- hb = more.offset().top + more.outerHeight(true);
- }
- }
-
- rb = results.offset().top + results.outerHeight(false);
- if (hb > rb) {
- results.scrollTop(results.scrollTop() + (hb - rb));
- }
- y = topOffset - results.offset().top;
-
- // make sure the top of the element is visible
- if (y < 0 && child.css('display') != 'none' ) {
- results.scrollTop(results.scrollTop() + y); // y is negative
- }
- },
-
- // abstract
- findHighlightableChoices: function() {
- return this.results.find(".select2-result-selectable:not(.select2-disabled):not(.select2-selected)");
- },
-
- // abstract
- moveHighlight: function (delta) {
- var choices = this.findHighlightableChoices(),
- index = this.highlight();
-
- while (index > -1 && index < choices.length) {
- index += delta;
- var choice = $(choices[index]);
- if (choice.hasClass("select2-result-selectable") && !choice.hasClass("select2-disabled") && !choice.hasClass("select2-selected")) {
- this.highlight(index);
- break;
- }
- }
- },
-
- // abstract
- highlight: function (index) {
- var choices = this.findHighlightableChoices(),
- choice,
- data;
-
- if (arguments.length === 0) {
- return indexOf(choices.filter(".select2-highlighted")[0], choices.get());
- }
-
- if (index >= choices.length) index = choices.length - 1;
- if (index < 0) index = 0;
-
- this.removeHighlight();
-
- choice = $(choices[index]);
- choice.addClass("select2-highlighted");
-
- // ensure assistive technology can determine the active choice
- this.search.attr("aria-activedescendant", choice.find(".select2-result-label").attr("id"));
-
- this.ensureHighlightVisible();
-
- this.liveRegion.text(choice.text());
-
- data = choice.data("select2-data");
- if (data) {
- this.opts.element.trigger({ type: "select2-highlight", val: this.id(data), choice: data });
- }
- },
-
- removeHighlight: function() {
- this.results.find(".select2-highlighted").removeClass("select2-highlighted");
- },
-
- touchMoved: function() {
- this._touchMoved = true;
- },
-
- clearTouchMoved: function() {
- this._touchMoved = false;
- },
-
- // abstract
- countSelectableResults: function() {
- return this.findHighlightableChoices().length;
- },
-
- // abstract
- highlightUnderEvent: function (event) {
- var el = $(event.target).closest(".select2-result-selectable");
- if (el.length > 0 && !el.is(".select2-highlighted")) {
- var choices = this.findHighlightableChoices();
- this.highlight(choices.index(el));
- } else if (el.length == 0) {
- // if we are over an unselectable item remove all highlights
- this.removeHighlight();
- }
- },
-
- // abstract
- loadMoreIfNeeded: function () {
- var results = this.results,
- more = results.find("li.select2-more-results"),
- below, // pixels the element is below the scroll fold, below==0 is when the element is starting to be visible
- page = this.resultsPage + 1,
- self=this,
- term=this.search.val(),
- context=this.context;
-
- if (more.length === 0) return;
- below = more.offset().top - results.offset().top - results.height();
-
- if (below <= this.opts.loadMorePadding) {
- more.addClass("select2-active");
- this.opts.query({
- element: this.opts.element,
- term: term,
- page: page,
- context: context,
- matcher: this.opts.matcher,
- callback: this.bind(function (data) {
-
- // ignore a response if the select2 has been closed before it was received
- if (!self.opened()) return;
-
-
- self.opts.populateResults.call(this, results, data.results, {term: term, page: page, context:context});
- self.postprocessResults(data, false, false);
-
- if (data.more===true) {
- more.detach().appendTo(results).html(self.opts.escapeMarkup(evaluate(self.opts.formatLoadMore, self.opts.element, page+1)));
- window.setTimeout(function() { self.loadMoreIfNeeded(); }, 10);
- } else {
- more.remove();
- }
- self.positionDropdown();
- self.resultsPage = page;
- self.context = data.context;
- this.opts.element.trigger({ type: "select2-loaded", items: data });
- })});
- }
- },
-
- /**
- * Default tokenizer function which does nothing
- */
- tokenize: function() {
-
- },
-
- /**
- * @param initial whether or not this is the call to this method right after the dropdown has been opened
- */
- // abstract
- updateResults: function (initial) {
- var search = this.search,
- results = this.results,
- opts = this.opts,
- data,
- self = this,
- input,
- term = search.val(),
- lastTerm = $.data(this.container, "select2-last-term"),
- // sequence number used to drop out-of-order responses
- queryNumber;
-
- // prevent duplicate queries against the same term
- if (initial !== true && lastTerm && equal(term, lastTerm)) return;
-
- $.data(this.container, "select2-last-term", term);
-
- // if the search is currently hidden we do not alter the results
- if (initial !== true && (this.showSearchInput === false || !this.opened())) {
- return;
- }
-
- function postRender() {
- search.removeClass("select2-active");
- self.positionDropdown();
- if (results.find('.select2-no-results,.select2-selection-limit,.select2-searching').length) {
- self.liveRegion.text(results.text());
- }
- else {
- self.liveRegion.text(self.opts.formatMatches(results.find('.select2-result-selectable:not(".select2-selected")').length));
- }
- }
-
- function render(html) {
- results.html(html);
- postRender();
- }
-
- queryNumber = ++this.queryCount;
-
- var maxSelSize = this.getMaximumSelectionSize();
- if (maxSelSize >=1) {
- data = this.data();
- if ($.isArray(data) && data.length >= maxSelSize && checkFormatter(opts.formatSelectionTooBig, "formatSelectionTooBig")) {
- render("" + evaluate(opts.formatSelectionTooBig, opts.element, maxSelSize) + " ");
- return;
- }
- }
-
- if (search.val().length < opts.minimumInputLength) {
- if (checkFormatter(opts.formatInputTooShort, "formatInputTooShort")) {
- render("" + evaluate(opts.formatInputTooShort, opts.element, search.val(), opts.minimumInputLength) + " ");
- } else {
- render("");
- }
- if (initial && this.showSearch) this.showSearch(true);
- return;
- }
-
- if (opts.maximumInputLength && search.val().length > opts.maximumInputLength) {
- if (checkFormatter(opts.formatInputTooLong, "formatInputTooLong")) {
- render("" + evaluate(opts.formatInputTooLong, opts.element, search.val(), opts.maximumInputLength) + " ");
- } else {
- render("");
- }
- return;
- }
-
- if (opts.formatSearching && this.findHighlightableChoices().length === 0) {
- render("" + evaluate(opts.formatSearching, opts.element) + " ");
- }
-
- search.addClass("select2-active");
-
- this.removeHighlight();
-
- // give the tokenizer a chance to pre-process the input
- input = this.tokenize();
- if (input != undefined && input != null) {
- search.val(input);
- }
-
- this.resultsPage = 1;
-
- opts.query({
- element: opts.element,
- term: search.val(),
- page: this.resultsPage,
- context: null,
- matcher: opts.matcher,
- callback: this.bind(function (data) {
- var def; // default choice
-
- // ignore old responses
- if (queryNumber != this.queryCount) {
- return;
- }
-
- // ignore a response if the select2 has been closed before it was received
- if (!this.opened()) {
- this.search.removeClass("select2-active");
- return;
- }
-
- // handle ajax error
- if(data.hasError !== undefined && checkFormatter(opts.formatAjaxError, "formatAjaxError")) {
- render("" + evaluate(opts.formatAjaxError, opts.element, data.jqXHR, data.textStatus, data.errorThrown) + " ");
- return;
- }
-
- // save context, if any
- this.context = (data.context===undefined) ? null : data.context;
- // create a default choice and prepend it to the list
- if (this.opts.createSearchChoice && search.val() !== "") {
- def = this.opts.createSearchChoice.call(self, search.val(), data.results);
- if (def !== undefined && def !== null && self.id(def) !== undefined && self.id(def) !== null) {
- if ($(data.results).filter(
- function () {
- return equal(self.id(this), self.id(def));
- }).length === 0) {
- this.opts.createSearchChoicePosition(data.results, def);
- }
- }
- }
-
- if (data.results.length === 0 && checkFormatter(opts.formatNoMatches, "formatNoMatches")) {
- render("" + evaluate(opts.formatNoMatches, opts.element, search.val()) + " ");
- if(this.showSearch){
- this.showSearch(search.val());
- }
- return;
- }
-
- results.empty();
- self.opts.populateResults.call(this, results, data.results, {term: search.val(), page: this.resultsPage, context:null});
-
- if (data.more === true && checkFormatter(opts.formatLoadMore, "formatLoadMore")) {
- results.append("" + opts.escapeMarkup(evaluate(opts.formatLoadMore, opts.element, this.resultsPage)) + " ");
- window.setTimeout(function() { self.loadMoreIfNeeded(); }, 10);
- }
-
- this.postprocessResults(data, initial);
-
- postRender();
-
- this.opts.element.trigger({ type: "select2-loaded", items: data });
- })});
- },
-
- // abstract
- cancel: function () {
- this.close();
- },
-
- // abstract
- blur: function () {
- // if selectOnBlur == true, select the currently highlighted option
- if (this.opts.selectOnBlur)
- this.selectHighlighted({noFocus: true});
-
- this.close();
- this.container.removeClass("select2-container-active");
- // synonymous to .is(':focus'), which is available in jquery >= 1.6
- if (this.search[0] === document.activeElement) { this.search.blur(); }
- this.clearSearch();
- this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus");
- },
-
- // abstract
- focusSearch: function () {
- focus(this.search);
- },
-
- // abstract
- selectHighlighted: function (options) {
- if (this._touchMoved) {
- this.clearTouchMoved();
- return;
- }
- var index=this.highlight(),
- highlighted=this.results.find(".select2-highlighted"),
- data = highlighted.closest('.select2-result').data("select2-data");
-
- if (data) {
- this.highlight(index);
- this.onSelect(data, options);
- } else if (options && options.noFocus) {
- this.close();
- }
- },
-
- // abstract
- getPlaceholder: function () {
- var placeholderOption;
- return this.opts.element.attr("placeholder") ||
- this.opts.element.attr("data-placeholder") || // jquery 1.4 compat
- this.opts.element.data("placeholder") ||
- this.opts.placeholder ||
- ((placeholderOption = this.getPlaceholderOption()) !== undefined ? placeholderOption.text() : undefined);
- },
-
- // abstract
- getPlaceholderOption: function() {
- if (this.select) {
- var firstOption = this.select.children('option').first();
- if (this.opts.placeholderOption !== undefined ) {
- //Determine the placeholder option based on the specified placeholderOption setting
- return (this.opts.placeholderOption === "first" && firstOption) ||
- (typeof this.opts.placeholderOption === "function" && this.opts.placeholderOption(this.select));
- } else if ($.trim(firstOption.text()) === "" && firstOption.val() === "") {
- //No explicit placeholder option specified, use the first if it's blank
- return firstOption;
- }
- }
- },
-
- /**
- * Get the desired width for the container element. This is
- * derived first from option `width` passed to select2, then
- * the inline 'style' on the original element, and finally
- * falls back to the jQuery calculated element width.
- */
- // abstract
- initContainerWidth: function () {
- function resolveContainerWidth() {
- var style, attrs, matches, i, l, attr;
-
- if (this.opts.width === "off") {
- return null;
- } else if (this.opts.width === "element"){
- return this.opts.element.outerWidth(false) === 0 ? 'auto' : this.opts.element.outerWidth(false) + 'px';
- } else if (this.opts.width === "copy" || this.opts.width === "resolve") {
- // check if there is inline style on the element that contains width
- style = this.opts.element.attr('style');
- if (typeof(style) === "string") {
- attrs = style.split(';');
- for (i = 0, l = attrs.length; i < l; i = i + 1) {
- attr = attrs[i].replace(/\s/g, '');
- matches = attr.match(/^width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/i);
- if (matches !== null && matches.length >= 1)
- return matches[1];
- }
- }
-
- if (this.opts.width === "resolve") {
- // next check if css('width') can resolve a width that is percent based, this is sometimes possible
- // when attached to input type=hidden or elements hidden via css
- style = this.opts.element.css('width');
- if (style.indexOf("%") > 0) return style;
-
- // finally, fallback on the calculated width of the element
- return (this.opts.element.outerWidth(false) === 0 ? 'auto' : this.opts.element.outerWidth(false) + 'px');
- }
-
- return null;
- } else if ($.isFunction(this.opts.width)) {
- return this.opts.width();
- } else {
- return this.opts.width;
- }
- };
-
- var width = resolveContainerWidth.call(this);
- if (width !== null) {
- this.container.css("width", width);
- }
- }
- });
-
- SingleSelect2 = clazz(AbstractSelect2, {
-
- // single
-
- createContainer: function () {
- var container = $(document.createElement("div")).attr({
- "class": "select2-container"
- }).html([
- "",
- " ",
- " ",
- " ",
- " ",
- " ",
- "",
- "
",
- " ",
- " ",
- "
",
- "
",
- "
"].join(""));
- return container;
- },
-
- // single
- enableInterface: function() {
- if (this.parent.enableInterface.apply(this, arguments)) {
- this.focusser.prop("disabled", !this.isInterfaceEnabled());
- }
- },
-
- // single
- opening: function () {
- var el, range, len;
-
- if (this.opts.minimumResultsForSearch >= 0) {
- this.showSearch(true);
- }
-
- this.parent.opening.apply(this, arguments);
-
- if (this.showSearchInput !== false) {
- // IE appends focusser.val() at the end of field :/ so we manually insert it at the beginning using a range
- // all other browsers handle this just fine
-
- this.search.val(this.focusser.val());
- }
- if (this.opts.shouldFocusInput(this)) {
- this.search.focus();
- // move the cursor to the end after focussing, otherwise it will be at the beginning and
- // new text will appear *before* focusser.val()
- el = this.search.get(0);
- if (el.createTextRange) {
- range = el.createTextRange();
- range.collapse(false);
- range.select();
- } else if (el.setSelectionRange) {
- len = this.search.val().length;
- el.setSelectionRange(len, len);
- }
- }
-
- this.prefillNextSearchTerm();
-
- this.focusser.prop("disabled", true).val("");
- this.updateResults(true);
- this.opts.element.trigger($.Event("select2-open"));
- },
-
- // single
- close: function () {
- if (!this.opened()) return;
- this.parent.close.apply(this, arguments);
-
- this.focusser.prop("disabled", false);
-
- if (this.opts.shouldFocusInput(this)) {
- this.focusser.focus();
- }
- },
-
- // single
- focus: function () {
- if (this.opened()) {
- this.close();
- } else {
- this.focusser.prop("disabled", false);
- if (this.opts.shouldFocusInput(this)) {
- this.focusser.focus();
- }
- }
- },
-
- // single
- isFocused: function () {
- return this.container.hasClass("select2-container-active");
- },
-
- // single
- cancel: function () {
- this.parent.cancel.apply(this, arguments);
- this.focusser.prop("disabled", false);
-
- if (this.opts.shouldFocusInput(this)) {
- this.focusser.focus();
- }
- },
-
- // single
- destroy: function() {
- $("label[for='" + this.focusser.attr('id') + "']")
- .attr('for', this.opts.element.attr("id"));
- this.parent.destroy.apply(this, arguments);
-
- cleanupJQueryElements.call(this,
- "selection",
- "focusser"
- );
- },
-
- // single
- initContainer: function () {
-
- var selection,
- container = this.container,
- dropdown = this.dropdown,
- idSuffix = nextUid(),
- elementLabel;
-
- if (this.opts.minimumResultsForSearch < 0) {
- this.showSearch(false);
- } else {
- this.showSearch(true);
- }
-
- this.selection = selection = container.find(".select2-choice");
-
- this.focusser = container.find(".select2-focusser");
-
- // add aria associations
- selection.find(".select2-chosen").attr("id", "select2-chosen-"+idSuffix);
- this.focusser.attr("aria-labelledby", "select2-chosen-"+idSuffix);
- this.results.attr("id", "select2-results-"+idSuffix);
- this.search.attr("aria-owns", "select2-results-"+idSuffix);
-
- // rewrite labels from original element to focusser
- this.focusser.attr("id", "s2id_autogen"+idSuffix);
-
- elementLabel = $("label[for='" + this.opts.element.attr("id") + "']");
- this.opts.element.on('focus.select2', this.bind(function () { this.focus(); }));
-
- this.focusser.prev()
- .text(elementLabel.text())
- .attr('for', this.focusser.attr('id'));
-
- // Ensure the original element retains an accessible name
- var originalTitle = this.opts.element.attr("title");
- this.opts.element.attr("title", (originalTitle || elementLabel.text()));
-
- this.focusser.attr("tabindex", this.elementTabIndex);
-
- // write label for search field using the label from the focusser element
- this.search.attr("id", this.focusser.attr('id') + '_search');
-
- this.search.prev()
- .text($("label[for='" + this.focusser.attr('id') + "']").text())
- .attr('for', this.search.attr('id'));
-
- this.search.on("keydown", this.bind(function (e) {
- if (!this.isInterfaceEnabled()) return;
-
- // filter 229 keyCodes (input method editor is processing key input)
- if (229 == e.keyCode) return;
-
- if (e.which === KEY.PAGE_UP || e.which === KEY.PAGE_DOWN) {
- // prevent the page from scrolling
- killEvent(e);
- return;
- }
-
- switch (e.which) {
- case KEY.UP:
- case KEY.DOWN:
- this.moveHighlight((e.which === KEY.UP) ? -1 : 1);
- killEvent(e);
- return;
- case KEY.ENTER:
- this.selectHighlighted();
- killEvent(e);
- return;
- case KEY.TAB:
- this.selectHighlighted({noFocus: true});
- return;
- case KEY.ESC:
- this.cancel(e);
- killEvent(e);
- return;
- }
- }));
-
- this.search.on("blur", this.bind(function(e) {
- // a workaround for chrome to keep the search field focussed when the scroll bar is used to scroll the dropdown.
- // without this the search field loses focus which is annoying
- if (document.activeElement === this.body.get(0)) {
- window.setTimeout(this.bind(function() {
- if (this.opened() && this.results && this.results.length > 1) {
- this.search.focus();
- }
- }), 0);
- }
- }));
-
- this.focusser.on("keydown", this.bind(function (e) {
- if (!this.isInterfaceEnabled()) return;
-
- if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.ESC) {
- return;
- }
-
- if (this.opts.openOnEnter === false && e.which === KEY.ENTER) {
- killEvent(e);
- return;
- }
-
- if (e.which == KEY.DOWN || e.which == KEY.UP
- || (e.which == KEY.ENTER && this.opts.openOnEnter)) {
-
- if (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) return;
-
- this.open();
- killEvent(e);
- return;
- }
-
- if (e.which == KEY.DELETE || e.which == KEY.BACKSPACE) {
- if (this.opts.allowClear) {
- this.clear();
- }
- killEvent(e);
- return;
- }
- }));
-
-
- installKeyUpChangeEvent(this.focusser);
- this.focusser.on("keyup-change input", this.bind(function(e) {
- if (this.opts.minimumResultsForSearch >= 0) {
- e.stopPropagation();
- if (this.opened()) return;
- this.open();
- }
- }));
-
- selection.on("mousedown touchstart", "abbr", this.bind(function (e) {
- if (!this.isInterfaceEnabled()) {
- return;
- }
-
- this.clear();
- killEventImmediately(e);
- this.close();
-
- if (this.selection) {
- this.selection.focus();
- }
- }));
-
- selection.on("mousedown touchstart", this.bind(function (e) {
- // Prevent IE from generating a click event on the body
- reinsertElement(selection);
-
- if (!this.container.hasClass("select2-container-active")) {
- this.opts.element.trigger($.Event("select2-focus"));
- }
-
- if (this.opened()) {
- this.close();
- } else if (this.isInterfaceEnabled()) {
- this.open();
- }
-
- killEvent(e);
- }));
-
- dropdown.on("mousedown touchstart", this.bind(function() {
- if (this.opts.shouldFocusInput(this)) {
- this.search.focus();
- }
- }));
-
- selection.on("focus", this.bind(function(e) {
- killEvent(e);
- }));
-
- this.focusser.on("focus", this.bind(function(){
- if (!this.container.hasClass("select2-container-active")) {
- this.opts.element.trigger($.Event("select2-focus"));
- }
- this.container.addClass("select2-container-active");
- })).on("blur", this.bind(function() {
- if (!this.opened()) {
- this.container.removeClass("select2-container-active");
- this.opts.element.trigger($.Event("select2-blur"));
- }
- }));
- this.search.on("focus", this.bind(function(){
- if (!this.container.hasClass("select2-container-active")) {
- this.opts.element.trigger($.Event("select2-focus"));
- }
- this.container.addClass("select2-container-active");
- }));
-
- this.initContainerWidth();
- this.opts.element.hide();
- this.setPlaceholder();
-
- },
-
- // single
- clear: function(triggerChange) {
- var data=this.selection.data("select2-data");
- if (data) { // guard against queued quick consecutive clicks
- var evt = $.Event("select2-clearing");
- this.opts.element.trigger(evt);
- if (evt.isDefaultPrevented()) {
- return;
- }
- var placeholderOption = this.getPlaceholderOption();
- this.opts.element.val(placeholderOption ? placeholderOption.val() : "");
- this.selection.find(".select2-chosen").empty();
- this.selection.removeData("select2-data");
- this.setPlaceholder();
-
- if (triggerChange !== false){
- this.opts.element.trigger({ type: "select2-removed", val: this.id(data), choice: data });
- this.triggerChange({removed:data});
- }
- }
- },
-
- /**
- * Sets selection based on source element's value
- */
- // single
- initSelection: function () {
- var selected;
- if (this.isPlaceholderOptionSelected()) {
- this.updateSelection(null);
- this.close();
- this.setPlaceholder();
- } else {
- var self = this;
- this.opts.initSelection.call(null, this.opts.element, function(selected){
- if (selected !== undefined && selected !== null) {
- self.updateSelection(selected);
- self.close();
- self.setPlaceholder();
- self.lastSearchTerm = self.search.val();
- }
- });
- }
- },
-
- isPlaceholderOptionSelected: function() {
- var placeholderOption;
- if (this.getPlaceholder() === undefined) return false; // no placeholder specified so no option should be considered
- return ((placeholderOption = this.getPlaceholderOption()) !== undefined && placeholderOption.prop("selected"))
- || (this.opts.element.val() === "")
- || (this.opts.element.val() === undefined)
- || (this.opts.element.val() === null);
- },
-
- // single
- prepareOpts: function () {
- var opts = this.parent.prepareOpts.apply(this, arguments),
- self=this;
-
- if (opts.element.get(0).tagName.toLowerCase() === "select") {
- // install the selection initializer
- opts.initSelection = function (element, callback) {
- var selected = element.find("option").filter(function() { return this.selected && !this.disabled });
- // a single select box always has a value, no need to null check 'selected'
- callback(self.optionToData(selected));
- };
- } else if ("data" in opts) {
- // install default initSelection when applied to hidden input and data is local
- opts.initSelection = opts.initSelection || function (element, callback) {
- var id = element.val();
- //search in data by id, storing the actual matching item
- var match = null;
- opts.query({
- matcher: function(term, text, el){
- var is_match = equal(id, opts.id(el));
- if (is_match) {
- match = el;
- }
- return is_match;
- },
- callback: !$.isFunction(callback) ? $.noop : function() {
- callback(match);
- }
- });
- };
- }
-
- return opts;
- },
-
- // single
- getPlaceholder: function() {
- // if a placeholder is specified on a single select without a valid placeholder option ignore it
- if (this.select) {
- if (this.getPlaceholderOption() === undefined) {
- return undefined;
- }
- }
-
- return this.parent.getPlaceholder.apply(this, arguments);
- },
-
- // single
- setPlaceholder: function () {
- var placeholder = this.getPlaceholder();
-
- if (this.isPlaceholderOptionSelected() && placeholder !== undefined) {
-
- // check for a placeholder option if attached to a select
- if (this.select && this.getPlaceholderOption() === undefined) return;
-
- this.selection.find(".select2-chosen").html(this.opts.escapeMarkup(placeholder));
-
- this.selection.addClass("select2-default");
-
- this.container.removeClass("select2-allowclear");
- }
- },
-
- // single
- postprocessResults: function (data, initial, noHighlightUpdate) {
- var selected = 0, self = this, showSearchInput = true;
-
- // find the selected element in the result list
-
- this.findHighlightableChoices().each2(function (i, elm) {
- if (equal(self.id(elm.data("select2-data")), self.opts.element.val())) {
- selected = i;
- return false;
- }
- });
-
- // and highlight it
- if (noHighlightUpdate !== false) {
- if (initial === true && selected >= 0) {
- this.highlight(selected);
- } else {
- this.highlight(0);
- }
- }
-
- // hide the search box if this is the first we got the results and there are enough of them for search
-
- if (initial === true) {
- var min = this.opts.minimumResultsForSearch;
- if (min >= 0) {
- this.showSearch(countResults(data.results) >= min);
- }
- }
- },
-
- // single
- showSearch: function(showSearchInput) {
- if (this.showSearchInput === showSearchInput) return;
-
- this.showSearchInput = showSearchInput;
-
- this.dropdown.find(".select2-search").toggleClass("select2-search-hidden", !showSearchInput);
- this.dropdown.find(".select2-search").toggleClass("select2-offscreen", !showSearchInput);
- //add "select2-with-searchbox" to the container if search box is shown
- $(this.dropdown, this.container).toggleClass("select2-with-searchbox", showSearchInput);
- },
-
- // single
- onSelect: function (data, options) {
-
- if (!this.triggerSelect(data)) { return; }
-
- var old = this.opts.element.val(),
- oldData = this.data();
-
- this.opts.element.val(this.id(data));
- this.updateSelection(data);
-
- this.opts.element.trigger({ type: "select2-selected", val: this.id(data), choice: data });
-
- this.lastSearchTerm = this.search.val();
- this.close();
-
- if ((!options || !options.noFocus) && this.opts.shouldFocusInput(this)) {
- this.focusser.focus();
- }
-
- if (!equal(old, this.id(data))) {
- this.triggerChange({ added: data, removed: oldData });
- }
- },
-
- // single
- updateSelection: function (data) {
-
- var container=this.selection.find(".select2-chosen"), formatted, cssClass;
-
- this.selection.data("select2-data", data);
-
- container.empty();
- if (data !== null) {
- formatted=this.opts.formatSelection(data, container, this.opts.escapeMarkup);
- }
- if (formatted !== undefined) {
- container.append(formatted);
- }
- cssClass=this.opts.formatSelectionCssClass(data, container);
- if (cssClass !== undefined) {
- container.addClass(cssClass);
- }
-
- this.selection.removeClass("select2-default");
-
- if (this.opts.allowClear && this.getPlaceholder() !== undefined) {
- this.container.addClass("select2-allowclear");
- }
- },
-
- // single
- val: function () {
- var val,
- triggerChange = false,
- data = null,
- self = this,
- oldData = this.data();
-
- if (arguments.length === 0) {
- return this.opts.element.val();
- }
-
- val = arguments[0];
-
- if (arguments.length > 1) {
- triggerChange = arguments[1];
-
- if (this.opts.debug && console && console.warn) {
- console.warn(
- 'Select2: The second option to `select2("val")` is not supported in Select2 4.0.0. ' +
- 'The `change` event will always be triggered in 4.0.0.'
- );
- }
- }
-
- if (this.select) {
- if (this.opts.debug && console && console.warn) {
- console.warn(
- 'Select2: Setting the value on a using `select2("val")` is no longer supported in 4.0.0. ' +
- 'You can use the `.val(newValue).trigger("change")` method provided by jQuery instead.'
- );
- }
-
- this.select
- .val(val)
- .find("option").filter(function() { return this.selected }).each2(function (i, elm) {
- data = self.optionToData(elm);
- return false;
- });
- this.updateSelection(data);
- this.setPlaceholder();
- if (triggerChange) {
- this.triggerChange({added: data, removed:oldData});
- }
- } else {
- // val is an id. !val is true for [undefined,null,'',0] - 0 is legal
- if (!val && val !== 0) {
- this.clear(triggerChange);
- return;
- }
- if (this.opts.initSelection === undefined) {
- throw new Error("cannot call val() if initSelection() is not defined");
- }
- this.opts.element.val(val);
- this.opts.initSelection(this.opts.element, function(data){
- self.opts.element.val(!data ? "" : self.id(data));
- self.updateSelection(data);
- self.setPlaceholder();
- if (triggerChange) {
- self.triggerChange({added: data, removed:oldData});
- }
- });
- }
- },
-
- // single
- clearSearch: function () {
- this.search.val("");
- this.focusser.val("");
- },
-
- // single
- data: function(value) {
- var data,
- triggerChange = false;
-
- if (arguments.length === 0) {
- data = this.selection.data("select2-data");
- if (data == undefined) data = null;
- return data;
- } else {
- if (this.opts.debug && console && console.warn) {
- console.warn(
- 'Select2: The `select2("data")` method can no longer set selected values in 4.0.0, ' +
- 'consider using the `.val()` method instead.'
- );
- }
-
- if (arguments.length > 1) {
- triggerChange = arguments[1];
- }
- if (!value) {
- this.clear(triggerChange);
- } else {
- data = this.data();
- this.opts.element.val(!value ? "" : this.id(value));
- this.updateSelection(value);
- if (triggerChange) {
- this.triggerChange({added: value, removed:data});
- }
- }
- }
- }
- });
-
- MultiSelect2 = clazz(AbstractSelect2, {
-
- // multi
- createContainer: function () {
- var container = $(document.createElement("div")).attr({
- "class": "select2-container select2-container-multi"
- }).html([
- "",
- ""].join(""));
- return container;
- },
-
- // multi
- prepareOpts: function () {
- var opts = this.parent.prepareOpts.apply(this, arguments),
- self=this;
-
- // TODO validate placeholder is a string if specified
- if (opts.element.get(0).tagName.toLowerCase() === "select") {
- // install the selection initializer
- opts.initSelection = function (element, callback) {
-
- var data = [];
-
- element.find("option").filter(function() { return this.selected && !this.disabled }).each2(function (i, elm) {
- data.push(self.optionToData(elm));
- });
- callback(data);
- };
- } else if ("data" in opts) {
- // install default initSelection when applied to hidden input and data is local
- opts.initSelection = opts.initSelection || function (element, callback) {
- var ids = splitVal(element.val(), opts.separator, opts.transformVal);
- //search in data by array of ids, storing matching items in a list
- var matches = [];
- opts.query({
- matcher: function(term, text, el){
- var is_match = $.grep(ids, function(id) {
- return equal(id, opts.id(el));
- }).length;
- if (is_match) {
- matches.push(el);
- }
- return is_match;
- },
- callback: !$.isFunction(callback) ? $.noop : function() {
- // reorder matches based on the order they appear in the ids array because right now
- // they are in the order in which they appear in data array
- var ordered = [];
- for (var i = 0; i < ids.length; i++) {
- var id = ids[i];
- for (var j = 0; j < matches.length; j++) {
- var match = matches[j];
- if (equal(id, opts.id(match))) {
- ordered.push(match);
- matches.splice(j, 1);
- break;
- }
- }
- }
- callback(ordered);
- }
- });
- };
- }
-
- return opts;
- },
-
- // multi
- selectChoice: function (choice) {
-
- var selected = this.container.find(".select2-search-choice-focus");
- if (selected.length && choice && choice[0] == selected[0]) {
-
- } else {
- if (selected.length) {
- this.opts.element.trigger("choice-deselected", selected);
- }
- selected.removeClass("select2-search-choice-focus");
- if (choice && choice.length) {
- this.close();
- choice.addClass("select2-search-choice-focus");
- this.opts.element.trigger("choice-selected", choice);
- }
- }
- },
-
- // multi
- destroy: function() {
- $("label[for='" + this.search.attr('id') + "']")
- .attr('for', this.opts.element.attr("id"));
- this.parent.destroy.apply(this, arguments);
-
- cleanupJQueryElements.call(this,
- "searchContainer",
- "selection"
- );
- },
-
- // multi
- initContainer: function () {
-
- var selector = ".select2-choices", selection;
-
- this.searchContainer = this.container.find(".select2-search-field");
- this.selection = selection = this.container.find(selector);
-
- var _this = this;
- this.selection.on("click", ".select2-container:not(.select2-container-disabled) .select2-search-choice:not(.select2-locked)", function (e) {
- _this.search[0].focus();
- _this.selectChoice($(this));
- });
-
- // rewrite labels from original element to focusser
- this.search.attr("id", "s2id_autogen"+nextUid());
-
- this.search.prev()
- .text($("label[for='" + this.opts.element.attr("id") + "']").text())
- .attr('for', this.search.attr('id'));
- this.opts.element.on('focus.select2', this.bind(function () { this.focus(); }));
-
- this.search.on("input paste", this.bind(function() {
- if (this.search.attr('placeholder') && this.search.val().length == 0) return;
- if (!this.isInterfaceEnabled()) return;
- if (!this.opened()) {
- this.open();
- }
- }));
-
- this.search.attr("tabindex", this.elementTabIndex);
-
- this.keydowns = 0;
- this.search.on("keydown", this.bind(function (e) {
- if (!this.isInterfaceEnabled()) return;
-
- ++this.keydowns;
- var selected = selection.find(".select2-search-choice-focus");
- var prev = selected.prev(".select2-search-choice:not(.select2-locked)");
- var next = selected.next(".select2-search-choice:not(.select2-locked)");
- var pos = getCursorInfo(this.search);
-
- if (selected.length &&
- (e.which == KEY.LEFT || e.which == KEY.RIGHT || e.which == KEY.BACKSPACE || e.which == KEY.DELETE || e.which == KEY.ENTER)) {
- var selectedChoice = selected;
- if (e.which == KEY.LEFT && prev.length) {
- selectedChoice = prev;
- }
- else if (e.which == KEY.RIGHT) {
- selectedChoice = next.length ? next : null;
- }
- else if (e.which === KEY.BACKSPACE) {
- if (this.unselect(selected.first())) {
- this.search.width(10);
- selectedChoice = prev.length ? prev : next;
- }
- } else if (e.which == KEY.DELETE) {
- if (this.unselect(selected.first())) {
- this.search.width(10);
- selectedChoice = next.length ? next : null;
- }
- } else if (e.which == KEY.ENTER) {
- selectedChoice = null;
- }
-
- this.selectChoice(selectedChoice);
- killEvent(e);
- if (!selectedChoice || !selectedChoice.length) {
- this.open();
- }
- return;
- } else if (((e.which === KEY.BACKSPACE && this.keydowns == 1)
- || e.which == KEY.LEFT) && (pos.offset == 0 && !pos.length)) {
-
- this.selectChoice(selection.find(".select2-search-choice:not(.select2-locked)").last());
- killEvent(e);
- return;
- } else {
- this.selectChoice(null);
- }
-
- if (this.opened()) {
- switch (e.which) {
- case KEY.UP:
- case KEY.DOWN:
- this.moveHighlight((e.which === KEY.UP) ? -1 : 1);
- killEvent(e);
- return;
- case KEY.ENTER:
- this.selectHighlighted();
- killEvent(e);
- return;
- case KEY.TAB:
- this.selectHighlighted({noFocus:true});
- this.close();
- return;
- case KEY.ESC:
- this.cancel(e);
- killEvent(e);
- return;
- }
- }
-
- if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e)
- || e.which === KEY.BACKSPACE || e.which === KEY.ESC) {
- return;
- }
-
- if (e.which === KEY.ENTER) {
- if (this.opts.openOnEnter === false) {
- return;
- } else if (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) {
- return;
- }
- }
-
- this.open();
-
- if (e.which === KEY.PAGE_UP || e.which === KEY.PAGE_DOWN) {
- // prevent the page from scrolling
- killEvent(e);
- }
-
- if (e.which === KEY.ENTER) {
- // prevent form from being submitted
- killEvent(e);
- }
-
- }));
-
- this.search.on("keyup", this.bind(function (e) {
- this.keydowns = 0;
- this.resizeSearch();
- })
- );
-
- this.search.on("blur", this.bind(function(e) {
- this.container.removeClass("select2-container-active");
- this.search.removeClass("select2-focused");
- this.selectChoice(null);
- if (!this.opened()) this.clearSearch();
- e.stopImmediatePropagation();
- this.opts.element.trigger($.Event("select2-blur"));
- }));
-
- this.container.on("click", selector, this.bind(function (e) {
- if (!this.isInterfaceEnabled()) return;
- if ($(e.target).closest(".select2-search-choice").length > 0) {
- // clicked inside a select2 search choice, do not open
- return;
- }
- this.selectChoice(null);
- this.clearPlaceholder();
- if (!this.container.hasClass("select2-container-active")) {
- this.opts.element.trigger($.Event("select2-focus"));
- }
- this.open();
- this.focusSearch();
- e.preventDefault();
- }));
-
- this.container.on("focus", selector, this.bind(function () {
- if (!this.isInterfaceEnabled()) return;
- if (!this.container.hasClass("select2-container-active")) {
- this.opts.element.trigger($.Event("select2-focus"));
- }
- this.container.addClass("select2-container-active");
- this.dropdown.addClass("select2-drop-active");
- this.clearPlaceholder();
- }));
-
- this.initContainerWidth();
- this.opts.element.hide();
-
- // set the placeholder if necessary
- this.clearSearch();
- },
-
- // multi
- enableInterface: function() {
- if (this.parent.enableInterface.apply(this, arguments)) {
- this.search.prop("disabled", !this.isInterfaceEnabled());
- }
- },
-
- // multi
- initSelection: function () {
- var data;
- if (this.opts.element.val() === "" && this.opts.element.text() === "") {
- this.updateSelection([]);
- this.close();
- // set the placeholder if necessary
- this.clearSearch();
- }
- if (this.select || this.opts.element.val() !== "") {
- var self = this;
- this.opts.initSelection.call(null, this.opts.element, function(data){
- if (data !== undefined && data !== null) {
- self.updateSelection(data);
- self.close();
- // set the placeholder if necessary
- self.clearSearch();
- }
- });
- }
- },
-
- // multi
- clearSearch: function () {
- var placeholder = this.getPlaceholder(),
- maxWidth = this.getMaxSearchWidth();
-
- if (placeholder !== undefined && this.getVal().length === 0 && this.search.hasClass("select2-focused") === false) {
- this.search.val(placeholder).addClass("select2-default");
- // stretch the search box to full width of the container so as much of the placeholder is visible as possible
- // we could call this.resizeSearch(), but we do not because that requires a sizer and we do not want to create one so early because of a firefox bug, see #944
- this.search.width(maxWidth > 0 ? maxWidth : this.container.css("width"));
- } else {
- this.search.val("").width(10);
- }
- },
-
- // multi
- clearPlaceholder: function () {
- if (this.search.hasClass("select2-default")) {
- this.search.val("").removeClass("select2-default");
- }
- },
-
- // multi
- opening: function () {
- this.clearPlaceholder(); // should be done before super so placeholder is not used to search
- this.resizeSearch();
-
- this.parent.opening.apply(this, arguments);
-
- this.focusSearch();
-
- this.prefillNextSearchTerm();
- this.updateResults(true);
-
- if (this.opts.shouldFocusInput(this)) {
- this.search.focus();
- }
- this.opts.element.trigger($.Event("select2-open"));
- },
-
- // multi
- close: function () {
- if (!this.opened()) return;
- this.parent.close.apply(this, arguments);
- },
-
- // multi
- focus: function () {
- this.close();
- this.search.focus();
- },
-
- // multi
- isFocused: function () {
- return this.search.hasClass("select2-focused");
- },
-
- // multi
- updateSelection: function (data) {
- var ids = {}, filtered = [], self = this;
-
- // filter out duplicates
- $(data).each(function () {
- if (!(self.id(this) in ids)) {
- ids[self.id(this)] = 0;
- filtered.push(this);
- }
- });
-
- this.selection.find(".select2-search-choice").remove();
- this.addSelectedChoice(filtered);
- self.postprocessResults();
- },
-
- // multi
- tokenize: function() {
- var input = this.search.val();
- input = this.opts.tokenizer.call(this, input, this.data(), this.bind(this.onSelect), this.opts);
- if (input != null && input != undefined) {
- this.search.val(input);
- if (input.length > 0) {
- this.open();
- }
- }
-
- },
-
- // multi
- onSelect: function (data, options) {
-
- if (!this.triggerSelect(data) || data.text === "") { return; }
-
- this.addSelectedChoice(data);
-
- this.opts.element.trigger({ type: "selected", val: this.id(data), choice: data });
-
- // keep track of the search's value before it gets cleared
- this.lastSearchTerm = this.search.val();
-
- this.clearSearch();
- this.updateResults();
-
- if (this.select || !this.opts.closeOnSelect) this.postprocessResults(data, false, this.opts.closeOnSelect===true);
-
- if (this.opts.closeOnSelect) {
- this.close();
- this.search.width(10);
- } else {
- if (this.countSelectableResults()>0) {
- this.search.width(10);
- this.resizeSearch();
- if (this.getMaximumSelectionSize() > 0 && this.val().length >= this.getMaximumSelectionSize()) {
- // if we reached max selection size repaint the results so choices
- // are replaced with the max selection reached message
- this.updateResults(true);
- } else {
- // initializes search's value with nextSearchTerm and update search result
- if (this.prefillNextSearchTerm()) {
- this.updateResults();
- }
- }
- this.positionDropdown();
- } else {
- // if nothing left to select close
- this.close();
- this.search.width(10);
- }
- }
-
- // since its not possible to select an element that has already been
- // added we do not need to check if this is a new element before firing change
- this.triggerChange({ added: data });
-
- if (!options || !options.noFocus)
- this.focusSearch();
- },
-
- // multi
- cancel: function () {
- this.close();
- this.focusSearch();
- },
-
- addSelectedChoice: function (data) {
- var val = this.getVal(), self = this;
- $(data).each(function () {
- val.push(self.createChoice(this));
- });
- this.setVal(val);
- },
-
- createChoice: function (data) {
- var enableChoice = !data.locked,
- enabledItem = $(
- "" +
- "
" +
- " " +
- " "),
- disabledItem = $(
- "" +
- "
" +
- " ");
- var choice = enableChoice ? enabledItem : disabledItem,
- id = this.id(data),
- formatted,
- cssClass;
-
- formatted=this.opts.formatSelection(data, choice.find("div"), this.opts.escapeMarkup);
- if (formatted != undefined) {
- choice.find("div").replaceWith($("
").html(formatted));
- }
- cssClass=this.opts.formatSelectionCssClass(data, choice.find("div"));
- if (cssClass != undefined) {
- choice.addClass(cssClass);
- }
-
- if(enableChoice){
- choice.find(".select2-search-choice-close")
- .on("mousedown", killEvent)
- .on("click dblclick", this.bind(function (e) {
- if (!this.isInterfaceEnabled()) return;
-
- this.unselect($(e.target));
- this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus");
- killEvent(e);
- this.close();
- this.focusSearch();
- })).on("focus", this.bind(function () {
- if (!this.isInterfaceEnabled()) return;
- this.container.addClass("select2-container-active");
- this.dropdown.addClass("select2-drop-active");
- }));
- }
-
- choice.data("select2-data", data);
- choice.insertBefore(this.searchContainer);
-
- return id;
- },
-
- // multi
- unselect: function (selected) {
- var val = this.getVal(),
- data,
- index;
- selected = selected.closest(".select2-search-choice");
-
- if (selected.length === 0) {
- throw "Invalid argument: " + selected + ". Must be .select2-search-choice";
- }
-
- data = selected.data("select2-data");
-
- if (!data) {
- // prevent a race condition when the 'x' is clicked really fast repeatedly the event can be queued
- // and invoked on an element already removed
- return;
- }
-
- var evt = $.Event("select2-removing");
- evt.val = this.id(data);
- evt.choice = data;
- this.opts.element.trigger(evt);
-
- if (evt.isDefaultPrevented()) {
- return false;
- }
-
- while((index = indexOf(this.id(data), val)) >= 0) {
- val.splice(index, 1);
- this.setVal(val);
- if (this.select) this.postprocessResults();
- }
-
- selected.remove();
-
- this.opts.element.trigger({ type: "select2-removed", val: this.id(data), choice: data });
- this.triggerChange({ removed: data });
-
- return true;
- },
-
- // multi
- postprocessResults: function (data, initial, noHighlightUpdate) {
- var val = this.getVal(),
- choices = this.results.find(".select2-result"),
- compound = this.results.find(".select2-result-with-children"),
- self = this;
-
- choices.each2(function (i, choice) {
- var id = self.id(choice.data("select2-data"));
- if (indexOf(id, val) >= 0) {
- choice.addClass("select2-selected");
- // mark all children of the selected parent as selected
- choice.find(".select2-result-selectable").addClass("select2-selected");
- }
- });
-
- compound.each2(function(i, choice) {
- // hide an optgroup if it doesn't have any selectable children
- if (!choice.is('.select2-result-selectable')
- && choice.find(".select2-result-selectable:not(.select2-selected)").length === 0) {
- choice.addClass("select2-selected");
- }
- });
-
- if (this.highlight() == -1 && noHighlightUpdate !== false && this.opts.closeOnSelect === true){
- self.highlight(0);
- }
-
- //If all results are chosen render formatNoMatches
- if(!this.opts.createSearchChoice && !choices.filter('.select2-result:not(.select2-selected)').length > 0){
- if(!data || data && !data.more && this.results.find(".select2-no-results").length === 0) {
- if (checkFormatter(self.opts.formatNoMatches, "formatNoMatches")) {
- this.results.append("" + evaluate(self.opts.formatNoMatches, self.opts.element, self.search.val()) + " ");
- }
- }
- }
-
- },
-
- // multi
- getMaxSearchWidth: function() {
- return this.selection.width() - getSideBorderPadding(this.search);
- },
-
- // multi
- resizeSearch: function () {
- var minimumWidth, left, maxWidth, containerLeft, searchWidth,
- sideBorderPadding = getSideBorderPadding(this.search);
-
- minimumWidth = measureTextWidth(this.search) + 10;
-
- left = this.search.offset().left;
-
- maxWidth = this.selection.width();
- containerLeft = this.selection.offset().left;
-
- searchWidth = maxWidth - (left - containerLeft) - sideBorderPadding;
-
- if (searchWidth < minimumWidth) {
- searchWidth = maxWidth - sideBorderPadding;
- }
-
- if (searchWidth < 40) {
- searchWidth = maxWidth - sideBorderPadding;
- }
-
- if (searchWidth <= 0) {
- searchWidth = minimumWidth;
- }
-
- this.search.width(Math.floor(searchWidth));
- },
-
- // multi
- getVal: function () {
- var val;
- if (this.select) {
- val = this.select.val();
- return val === null ? [] : val;
- } else {
- val = this.opts.element.val();
- return splitVal(val, this.opts.separator, this.opts.transformVal);
- }
- },
-
- // multi
- setVal: function (val) {
- if (this.select) {
- this.select.val(val);
- } else {
- var unique = [], valMap = {};
- // filter out duplicates
- $(val).each(function () {
- if (!(this in valMap)) {
- unique.push(this);
- valMap[this] = 0;
- }
- });
- this.opts.element.val(unique.length === 0 ? "" : unique.join(this.opts.separator));
- }
- },
-
- // multi
- buildChangeDetails: function (old, current) {
- var current = current.slice(0),
- old = old.slice(0);
-
- // remove intersection from each array
- for (var i = 0; i < current.length; i++) {
- for (var j = 0; j < old.length; j++) {
- if (equal(this.opts.id(current[i]), this.opts.id(old[j]))) {
- current.splice(i, 1);
- i--;
- old.splice(j, 1);
- break;
- }
- }
- }
-
- return {added: current, removed: old};
- },
-
-
- // multi
- val: function (val, triggerChange) {
- var oldData, self=this;
-
- if (arguments.length === 0) {
- return this.getVal();
- }
-
- oldData=this.data();
- if (!oldData.length) oldData=[];
-
- // val is an id. !val is true for [undefined,null,'',0] - 0 is legal
- if (!val && val !== 0) {
- this.opts.element.val("");
- this.updateSelection([]);
- this.clearSearch();
- if (triggerChange) {
- this.triggerChange({added: this.data(), removed: oldData});
- }
- return;
- }
-
- // val is a list of ids
- this.setVal(val);
-
- if (this.select) {
- this.opts.initSelection(this.select, this.bind(this.updateSelection));
- if (triggerChange) {
- this.triggerChange(this.buildChangeDetails(oldData, this.data()));
- }
- } else {
- if (this.opts.initSelection === undefined) {
- throw new Error("val() cannot be called if initSelection() is not defined");
- }
-
- this.opts.initSelection(this.opts.element, function(data){
- var ids=$.map(data, self.id);
- self.setVal(ids);
- self.updateSelection(data);
- self.clearSearch();
- if (triggerChange) {
- self.triggerChange(self.buildChangeDetails(oldData, self.data()));
- }
- });
- }
- this.clearSearch();
- },
-
- // multi
- onSortStart: function() {
- if (this.select) {
- throw new Error("Sorting of elements is not supported when attached to . Attach to instead.");
- }
-
- // collapse search field into 0 width so its container can be collapsed as well
- this.search.width(0);
- // hide the container
- this.searchContainer.hide();
- },
-
- // multi
- onSortEnd:function() {
-
- var val=[], self=this;
-
- // show search and move it to the end of the list
- this.searchContainer.show();
- // make sure the search container is the last item in the list
- this.searchContainer.appendTo(this.searchContainer.parent());
- // since we collapsed the width in dragStarted, we resize it here
- this.resizeSearch();
-
- // update selection
- this.selection.find(".select2-search-choice").each(function() {
- val.push(self.opts.id($(this).data("select2-data")));
- });
- this.setVal(val);
- this.triggerChange();
- },
-
- // multi
- data: function(values, triggerChange) {
- var self=this, ids, old;
- if (arguments.length === 0) {
- return this.selection
- .children(".select2-search-choice")
- .map(function() { return $(this).data("select2-data"); })
- .get();
- } else {
- old = this.data();
- if (!values) { values = []; }
- ids = $.map(values, function(e) { return self.opts.id(e); });
- this.setVal(ids);
- this.updateSelection(values);
- this.clearSearch();
- if (triggerChange) {
- this.triggerChange(this.buildChangeDetails(old, this.data()));
- }
- }
- }
- });
-
- $.fn.select2 = function () {
-
- var args = Array.prototype.slice.call(arguments, 0),
- opts,
- select2,
- method, value, multiple,
- allowedMethods = ["val", "destroy", "opened", "open", "close", "focus", "isFocused", "container", "dropdown", "onSortStart", "onSortEnd", "enable", "disable", "readonly", "positionDropdown", "data", "search"],
- valueMethods = ["opened", "isFocused", "container", "dropdown"],
- propertyMethods = ["val", "data"],
- methodsMap = { search: "externalSearch" };
-
- this.each(function () {
- if (args.length === 0 || typeof(args[0]) === "object") {
- opts = args.length === 0 ? {} : $.extend({}, args[0]);
- opts.element = $(this);
-
- if (opts.element.get(0).tagName.toLowerCase() === "select") {
- multiple = opts.element.prop("multiple");
- } else {
- multiple = opts.multiple || false;
- if ("tags" in opts) {opts.multiple = multiple = true;}
- }
-
- select2 = multiple ? new window.Select2["class"].multi() : new window.Select2["class"].single();
- select2.init(opts);
- } else if (typeof(args[0]) === "string") {
-
- if (indexOf(args[0], allowedMethods) < 0) {
- throw "Unknown method: " + args[0];
- }
-
- value = undefined;
- select2 = $(this).data("select2");
- if (select2 === undefined) return;
-
- method=args[0];
-
- if (method === "container") {
- value = select2.container;
- } else if (method === "dropdown") {
- value = select2.dropdown;
- } else {
- if (methodsMap[method]) method = methodsMap[method];
-
- value = select2[method].apply(select2, args.slice(1));
- }
- if (indexOf(args[0], valueMethods) >= 0
- || (indexOf(args[0], propertyMethods) >= 0 && args.length == 1)) {
- return false; // abort the iteration, ready to return first matched value
- }
- } else {
- throw "Invalid arguments to select2 plugin: " + args;
- }
- });
- return (value === undefined) ? this : value;
- };
-
- // plugin defaults, accessible to users
- $.fn.select2.defaults = {
- debug: false,
- width: "copy",
- loadMorePadding: 0,
- closeOnSelect: true,
- openOnEnter: true,
- containerCss: {},
- dropdownCss: {},
- containerCssClass: "",
- dropdownCssClass: "",
- formatResult: function(result, container, query, escapeMarkup) {
- var markup=[];
- markMatch(this.text(result), query.term, markup, escapeMarkup);
- return markup.join("");
- },
- transformVal: function(val) {
- return $.trim(val);
- },
- formatSelection: function (data, container, escapeMarkup) {
- return data ? escapeMarkup(this.text(data)) : undefined;
- },
- sortResults: function (results, container, query) {
- return results;
- },
- formatResultCssClass: function(data) {return data.css;},
- formatSelectionCssClass: function(data, container) {return undefined;},
- minimumResultsForSearch: 0,
- minimumInputLength: 0,
- maximumInputLength: null,
- maximumSelectionSize: 0,
- id: function (e) { return e == undefined ? null : e.id; },
- text: function (e) {
- if (e && this.data && this.data.text) {
- if ($.isFunction(this.data.text)) {
- return this.data.text(e);
- } else {
- return e[this.data.text];
- }
- } else {
- return e.text;
- }
- },
- matcher: function(term, text) {
- return stripDiacritics(''+text).toUpperCase().indexOf(stripDiacritics(''+term).toUpperCase()) >= 0;
- },
- separator: ",",
- tokenSeparators: [],
- tokenizer: defaultTokenizer,
- escapeMarkup: defaultEscapeMarkup,
- blurOnChange: false,
- selectOnBlur: false,
- adaptContainerCssClass: function(c) { return c; },
- adaptDropdownCssClass: function(c) { return null; },
- nextSearchTerm: function(selectedObject, currentSearchTerm) { return undefined; },
- searchInputPlaceholder: '',
- createSearchChoicePosition: 'top',
- shouldFocusInput: function (instance) {
- // Attempt to detect touch devices
- var supportsTouchEvents = (('ontouchstart' in window) ||
- (navigator.msMaxTouchPoints > 0));
-
- // Only devices which support touch events should be special cased
- if (!supportsTouchEvents) {
- return true;
- }
-
- // Never focus the input if search is disabled
- if (instance.opts.minimumResultsForSearch < 0) {
- return false;
- }
-
- return true;
- }
- };
-
- $.fn.select2.locales = [];
-
- $.fn.select2.locales['en'] = {
- formatMatches: function (matches) { if (matches === 1) { return "One result is available, press enter to select it."; } return matches + " results are available, use up and down arrow keys to navigate."; },
- formatNoMatches: function () { return "No matches found"; },
- formatAjaxError: function (jqXHR, textStatus, errorThrown) { return "Loading failed"; },
- formatInputTooShort: function (input, min) { var n = min - input.length; return "Please enter " + n + " or more character" + (n == 1 ? "" : "s"); },
- formatInputTooLong: function (input, max) { var n = input.length - max; return "Please delete " + n + " character" + (n == 1 ? "" : "s"); },
- formatSelectionTooBig: function (limit) { return "You can only select " + limit + " item" + (limit == 1 ? "" : "s"); },
- formatLoadMore: function (pageNumber) { return "Loading more results…"; },
- formatSearching: function () { return "Searching…"; }
- };
-
- $.extend($.fn.select2.defaults, $.fn.select2.locales['en']);
-
- $.fn.select2.ajaxDefaults = {
- transport: $.ajax,
- params: {
- type: "GET",
- cache: false,
- dataType: "json"
- }
- };
-
- // exports
- window.Select2 = {
- query: {
- ajax: ajax,
- local: local,
- tags: tags
- }, util: {
- debounce: debounce,
- markMatch: markMatch,
- escapeMarkup: defaultEscapeMarkup,
- stripDiacritics: stripDiacritics
- }, "class": {
- "abstract": AbstractSelect2,
- "single": SingleSelect2,
- "multi": MultiSelect2
- }
- };
-
-}(jQuery));