|
@@ -24,7 +24,9 @@
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- var KEY = {
|
|
|
+ var KEY, AbstractSelect2, SingleSelect2, MultiSelect2;
|
|
|
+
|
|
|
+ KEY = {
|
|
|
TAB: 9,
|
|
|
ENTER: 13,
|
|
|
ESC: 27,
|
|
@@ -298,1063 +300,1066 @@
|
|
|
});
|
|
|
});
|
|
|
|
|
|
+
|
|
|
/**
|
|
|
+ * Creates a new class
|
|
|
*
|
|
|
- * @param opts
|
|
|
+ * @param superClass
|
|
|
+ * @param methods
|
|
|
*/
|
|
|
- function AbstractSelect2() {
|
|
|
+ function clazz(superClass, methods) {
|
|
|
+ var clazz = function () {};
|
|
|
+ clazz.prototype = new superClass;
|
|
|
+ clazz.prototype.constructor = clazz;
|
|
|
+ clazz.prototype.parent = superClass.prototype;
|
|
|
+ clazz.prototype = $.extend(clazz.prototype, methods);
|
|
|
+ return clazz;
|
|
|
}
|
|
|
|
|
|
- AbstractSelect2.prototype.bind = function (func) {
|
|
|
- var self = this;
|
|
|
- return function () {
|
|
|
- func.apply(self, arguments);
|
|
|
- };
|
|
|
- };
|
|
|
-
|
|
|
- AbstractSelect2.prototype.init = function (opts) {
|
|
|
- var results, search, resultsSelector = ".select2-results";
|
|
|
-
|
|
|
- // prepare options
|
|
|
- this.opts = this.prepareOpts(opts);
|
|
|
+ AbstractSelect2 = clazz(Object, {
|
|
|
|
|
|
- // destroy if called on an existing component
|
|
|
- if (opts.element.data("select2") !== undefined) {
|
|
|
- this.destroy();
|
|
|
- }
|
|
|
+ bind: function (func) {
|
|
|
+ var self = this;
|
|
|
+ return function () {
|
|
|
+ func.apply(self, arguments);
|
|
|
+ };
|
|
|
+ },
|
|
|
|
|
|
- this.container = this.createContainer();
|
|
|
+ init: function (opts) {
|
|
|
+ var results, search, resultsSelector = ".select2-results";
|
|
|
|
|
|
- if (opts.element.attr("class") !== undefined) {
|
|
|
- this.container.addClass(opts.element.attr("class"));
|
|
|
- }
|
|
|
+ // prepare options
|
|
|
+ this.opts = this.prepareOpts(opts);
|
|
|
|
|
|
- // swap container for the element
|
|
|
- this.opts.element
|
|
|
- .data("select2", this)
|
|
|
- .hide()
|
|
|
- .after(this.container);
|
|
|
- this.container.data("select2", this);
|
|
|
-
|
|
|
- this.dropdown = this.container.find(".select2-drop");
|
|
|
- this.results = results = this.container.find(resultsSelector);
|
|
|
- this.search = search = this.container.find("input[type=text]");
|
|
|
+ // destroy if called on an existing component
|
|
|
+ if (opts.element.data("select2") !== undefined) {
|
|
|
+ this.destroy();
|
|
|
+ }
|
|
|
|
|
|
- this.resultsPage = 0;
|
|
|
+ this.container = this.createContainer();
|
|
|
|
|
|
- // initialize the container
|
|
|
- this.initContainer();
|
|
|
+ if (opts.element.attr("class") !== undefined) {
|
|
|
+ this.container.addClass(opts.element.attr("class"));
|
|
|
+ }
|
|
|
|
|
|
- installFilteredMouseMove(this.results);
|
|
|
- this.container.delegate(resultsSelector, "mousemove-filtered", this.bind(this.highlightUnderEvent));
|
|
|
+ // swap container for the element
|
|
|
+ this.opts.element
|
|
|
+ .data("select2", this)
|
|
|
+ .hide()
|
|
|
+ .after(this.container);
|
|
|
+ this.container.data("select2", this);
|
|
|
+
|
|
|
+ this.dropdown = this.container.find(".select2-drop");
|
|
|
+ this.results = results = this.container.find(resultsSelector);
|
|
|
+ this.search = search = this.container.find("input[type=text]");
|
|
|
+
|
|
|
+ this.resultsPage = 0;
|
|
|
+
|
|
|
+ // initialize the container
|
|
|
+ this.initContainer();
|
|
|
+
|
|
|
+ installFilteredMouseMove(this.results);
|
|
|
+ this.container.delegate(resultsSelector, "mousemove-filtered", this.bind(this.highlightUnderEvent));
|
|
|
+
|
|
|
+ installDebouncedScroll(80, this.results);
|
|
|
+ this.container.delegate(resultsSelector, "scroll-debounced", this.bind(this.loadMoreIfNeeded));
|
|
|
+
|
|
|
+ // if jquery.mousewheel plugin is installed we can prevent out-of-bounds scrolling of results via mousewheel
|
|
|
+ if ($.fn.mousewheel) {
|
|
|
+ results.mousewheel(function (e, delta, deltaX, deltaY) {
|
|
|
+ var top = results.scrollTop(), height;
|
|
|
+ if (deltaY > 0 && top - deltaY <= 0) {
|
|
|
+ results.scrollTop(0);
|
|
|
+ killEvent(e);
|
|
|
+ } else if (deltaY < 0 && results.get(0).scrollHeight - results.scrollTop() + deltaY <= results.height()) {
|
|
|
+ results.scrollTop(results.get(0).scrollHeight - results.height());
|
|
|
+ killEvent(e);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
|
|
|
- installDebouncedScroll(80, this.results);
|
|
|
- this.container.delegate(resultsSelector, "scroll-debounced", this.bind(this.loadMoreIfNeeded));
|
|
|
+ installKeyUpChangeEvent(search);
|
|
|
+ search.bind("keyup-change", this.bind(this.updateResults));
|
|
|
|
|
|
- // if jquery.mousewheel plugin is installed we can prevent out-of-bounds scrolling of results via mousewheel
|
|
|
- if ($.fn.mousewheel) {
|
|
|
- results.mousewheel(function (e, delta, deltaX, deltaY) {
|
|
|
- var top = results.scrollTop(), height;
|
|
|
- if (deltaY > 0 && top - deltaY <= 0) {
|
|
|
- results.scrollTop(0);
|
|
|
- killEvent(e);
|
|
|
- } else if (deltaY < 0 && results.get(0).scrollHeight - results.scrollTop() + deltaY <= results.height()) {
|
|
|
- results.scrollTop(results.get(0).scrollHeight - results.height());
|
|
|
+ this.container.delegate(resultsSelector, "click", this.bind(function (e) {
|
|
|
+ if ($(e.target).closest(".select2-result:not(.select2-disabled)").length > 0) {
|
|
|
+ this.highlightUnderEvent(e);
|
|
|
+ this.selectHighlighted(e);
|
|
|
+ } else {
|
|
|
killEvent(e);
|
|
|
+ this.focusSearch();
|
|
|
}
|
|
|
- });
|
|
|
- }
|
|
|
+ }));
|
|
|
|
|
|
- installKeyUpChangeEvent(search);
|
|
|
- search.bind("keyup-change", this.bind(this.updateResults));
|
|
|
+ if ($.isFunction(this.opts.initSelection)) {
|
|
|
+ // initialize selection based on the current value of the source element
|
|
|
+ this.initSelection();
|
|
|
|
|
|
- this.container.delegate(resultsSelector, "click", this.bind(function (e) {
|
|
|
- if ($(e.target).closest(".select2-result:not(.select2-disabled)").length > 0) {
|
|
|
- this.highlightUnderEvent(e);
|
|
|
- this.selectHighlighted(e);
|
|
|
- } else {
|
|
|
- killEvent(e);
|
|
|
- this.focusSearch();
|
|
|
+ // if 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 ($.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();
|
|
|
- }
|
|
|
- };
|
|
|
+ },
|
|
|
|
|
|
- AbstractSelect2.prototype.destroy = function () {
|
|
|
- var select2 = this.opts.element.data("select2");
|
|
|
- if (select2 !== undefined) {
|
|
|
- select2.container.remove();
|
|
|
- select2.opts.element
|
|
|
- .removeData("select2")
|
|
|
- .show();
|
|
|
- }
|
|
|
- };
|
|
|
+ destroy: function () {
|
|
|
+ var select2 = this.opts.element.data("select2");
|
|
|
+ if (select2 !== undefined) {
|
|
|
+ select2.container.remove();
|
|
|
+ select2.opts.element
|
|
|
+ .removeData("select2")
|
|
|
+ .show();
|
|
|
+ }
|
|
|
+ },
|
|
|
|
|
|
- AbstractSelect2.prototype.prepareOpts = function (opts) {
|
|
|
- var element, select;
|
|
|
+ prepareOpts: function (opts) {
|
|
|
+ var element, select;
|
|
|
|
|
|
- opts = $.extend({}, {
|
|
|
- formatResult: function (data) { return data.text; },
|
|
|
- formatSelection: function (data) { return data.text; },
|
|
|
- formatNoMatches: function () { return "No matches found"; },
|
|
|
- formatInputTooShort: function (input, min) { return "Please enter " + (min - input.length) + " more characters"; },
|
|
|
- minimumResultsForSearch: 0
|
|
|
- }, opts);
|
|
|
+ opts = $.extend({}, {
|
|
|
+ formatResult: function (data) { return data.text; },
|
|
|
+ formatSelection: function (data) { return data.text; },
|
|
|
+ formatNoMatches: function () { return "No matches found"; },
|
|
|
+ formatInputTooShort: function (input, min) { return "Please enter " + (min - input.length) + " more characters"; },
|
|
|
+ minimumResultsForSearch: 0
|
|
|
+ }, opts);
|
|
|
|
|
|
- element = opts.element;
|
|
|
+ element = opts.element;
|
|
|
|
|
|
- if (element.get(0).tagName.toLowerCase() === "select") {
|
|
|
- this.select = select = opts.element;
|
|
|
- }
|
|
|
+ if (element.get(0).tagName.toLowerCase() === "select") {
|
|
|
+ this.select = select = opts.element;
|
|
|
+ }
|
|
|
|
|
|
- // TODO add missing validation logic
|
|
|
- if (select) {
|
|
|
- /*$.each(["multiple", "ajax", "query", "minimumInputLength"], function () {
|
|
|
- if (this in opts) {
|
|
|
- throw "Option '" + this + "' is not allowed for Select2 when attached to a select element";
|
|
|
- }
|
|
|
- });*/
|
|
|
- this.opts = opts = $.extend({}, {
|
|
|
- miniumInputLength: 0
|
|
|
- }, opts);
|
|
|
- } else {
|
|
|
- this.opts = opts = $.extend({}, {
|
|
|
- miniumInputLength: 0
|
|
|
- }, opts);
|
|
|
- }
|
|
|
+ // TODO add missing validation logic
|
|
|
+ if (select) {
|
|
|
+ /*$.each(["multiple", "ajax", "query", "minimumInputLength"], function () {
|
|
|
+ if (this in opts) {
|
|
|
+ throw "Option '" + this + "' is not allowed for Select2 when attached to a select element";
|
|
|
+ }
|
|
|
+ });*/
|
|
|
+ this.opts = opts = $.extend({}, {
|
|
|
+ miniumInputLength: 0
|
|
|
+ }, opts);
|
|
|
+ } else {
|
|
|
+ this.opts = opts = $.extend({}, {
|
|
|
+ miniumInputLength: 0
|
|
|
+ }, opts);
|
|
|
+ }
|
|
|
|
|
|
- if (select) {
|
|
|
- opts.query = this.bind(function (query) {
|
|
|
- var data = {results: [], more: false},
|
|
|
- term = query.term.toUpperCase(),
|
|
|
- placeholder = this.getPlaceholder();
|
|
|
- element.find("option").each(function (i) {
|
|
|
- var e = $(this),
|
|
|
- text = e.text();
|
|
|
+ if (select) {
|
|
|
+ opts.query = this.bind(function (query) {
|
|
|
+ var data = {results: [], more: false},
|
|
|
+ term = query.term.toUpperCase(),
|
|
|
+ placeholder = this.getPlaceholder();
|
|
|
+ element.find("option").each(function (i) {
|
|
|
+ var e = $(this),
|
|
|
+ text = e.text();
|
|
|
|
|
|
- if (i === 0 && placeholder !== undefined && text === "") return true;
|
|
|
+ if (i === 0 && placeholder !== undefined && text === "") return true;
|
|
|
|
|
|
- if (text.toUpperCase().indexOf(term) >= 0) {
|
|
|
- data.results.push({id: e.attr("value"), text: text});
|
|
|
- }
|
|
|
+ if (text.toUpperCase().indexOf(term) >= 0) {
|
|
|
+ data.results.push({id: e.attr("value"), text: text});
|
|
|
+ }
|
|
|
+ });
|
|
|
+ query.callback(data);
|
|
|
});
|
|
|
- query.callback(data);
|
|
|
- });
|
|
|
- } else {
|
|
|
- if (!("query" in opts)) {
|
|
|
- if ("ajax" in opts) {
|
|
|
- opts.query = ajax(opts.ajax);
|
|
|
- } else if ("data" in opts) {
|
|
|
- opts.query = local(opts.data);
|
|
|
- } else if ("tags" in opts) {
|
|
|
- opts.query = tags(opts.tags);
|
|
|
- opts.createSearchChoice = function (term) { return {id: term, text: term};}
|
|
|
- opts.initSelection = function (element) {
|
|
|
- var data = [];
|
|
|
- $(element.val().split(",")).each(function () {
|
|
|
- data.push({id: this, text: this});
|
|
|
- });
|
|
|
- return data;
|
|
|
+ } else {
|
|
|
+ if (!("query" in opts)) {
|
|
|
+ if ("ajax" in opts) {
|
|
|
+ opts.query = ajax(opts.ajax);
|
|
|
+ } else if ("data" in opts) {
|
|
|
+ opts.query = local(opts.data);
|
|
|
+ } else if ("tags" in opts) {
|
|
|
+ opts.query = tags(opts.tags);
|
|
|
+ opts.createSearchChoice = function (term) { return {id: term, text: term};}
|
|
|
+ opts.initSelection = function (element) {
|
|
|
+ var data = [];
|
|
|
+ $(element.val().split(",")).each(function () {
|
|
|
+ data.push({id: this, text: this});
|
|
|
+ });
|
|
|
+ return data;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- }
|
|
|
- if (typeof(opts.query) !== "function") {
|
|
|
- throw "query function not defined for Select2 " + opts.element.attr("id");
|
|
|
- }
|
|
|
-
|
|
|
- return opts;
|
|
|
- };
|
|
|
-
|
|
|
- /**
|
|
|
- * Monitor the original element for changes and update select2 accordingly
|
|
|
- */
|
|
|
- AbstractSelect2.prototype.monitorSource = function () {
|
|
|
- this.opts.element.bind("change", this.bind(function (e) {
|
|
|
- if (this.opts.element.data("select2-change-triggered") !== true) {
|
|
|
- this.initSelection();
|
|
|
+ if (typeof(opts.query) !== "function") {
|
|
|
+ throw "query function not defined for Select2 " + opts.element.attr("id");
|
|
|
}
|
|
|
- }));
|
|
|
- };
|
|
|
-
|
|
|
- /**
|
|
|
- * Triggers the change event on the source element
|
|
|
- */
|
|
|
- AbstractSelect2.prototype.triggerChange = function () {
|
|
|
- // Prevents recursive triggering
|
|
|
- this.opts.element.data("select2-change-triggered", true);
|
|
|
- this.opts.element.trigger("change");
|
|
|
- this.opts.element.data("select2-change-triggered", false);
|
|
|
- };
|
|
|
|
|
|
- AbstractSelect2.prototype.opened = function () {
|
|
|
- return this.container.hasClass("select2-dropdown-open");
|
|
|
- };
|
|
|
+ return opts;
|
|
|
+ },
|
|
|
|
|
|
- AbstractSelect2.prototype.alignDropdown = function () {
|
|
|
- this.dropdown.css({
|
|
|
- top: this.container.height()
|
|
|
- });
|
|
|
- };
|
|
|
+ /**
|
|
|
+ * Monitor the original element for changes and update select2 accordingly
|
|
|
+ */
|
|
|
+ monitorSource: function () {
|
|
|
+ this.opts.element.bind("change", this.bind(function (e) {
|
|
|
+ if (this.opts.element.data("select2-change-triggered") !== true) {
|
|
|
+ this.initSelection();
|
|
|
+ }
|
|
|
+ }));
|
|
|
+ },
|
|
|
|
|
|
- AbstractSelect2.prototype.open = function () {
|
|
|
- if (this.opened()) return;
|
|
|
+ /**
|
|
|
+ * Triggers the change event on the source element
|
|
|
+ */
|
|
|
+ triggerChange: function () {
|
|
|
+ // Prevents recursive triggering
|
|
|
+ this.opts.element.data("select2-change-triggered", true);
|
|
|
+ this.opts.element.trigger("change");
|
|
|
+ this.opts.element.data("select2-change-triggered", false);
|
|
|
+ },
|
|
|
|
|
|
- this.container.addClass("select2-dropdown-open").addClass("select2-container-active");
|
|
|
+ opened: function () {
|
|
|
+ return this.container.hasClass("select2-dropdown-open");
|
|
|
+ },
|
|
|
|
|
|
- this.updateResults(true);
|
|
|
- this.alignDropdown();
|
|
|
- this.dropdown.show();
|
|
|
- this.focusSearch();
|
|
|
- };
|
|
|
+ alignDropdown: function () {
|
|
|
+ this.dropdown.css({
|
|
|
+ top: this.container.height()
|
|
|
+ });
|
|
|
+ },
|
|
|
|
|
|
- AbstractSelect2.prototype.close = function () {
|
|
|
- if (!this.opened()) return;
|
|
|
+ open: function () {
|
|
|
+ if (this.opened()) return;
|
|
|
|
|
|
- this.dropdown.hide();
|
|
|
- this.container.removeClass("select2-dropdown-open");
|
|
|
- this.results.empty();
|
|
|
- this.clearSearch();
|
|
|
- };
|
|
|
+ this.container.addClass("select2-dropdown-open").addClass("select2-container-active");
|
|
|
|
|
|
- AbstractSelect2.prototype.clearSearch = function () {
|
|
|
+ this.updateResults(true);
|
|
|
+ this.alignDropdown();
|
|
|
+ this.dropdown.show();
|
|
|
+ this.focusSearch();
|
|
|
+ },
|
|
|
|
|
|
- };
|
|
|
+ close: function () {
|
|
|
+ if (!this.opened()) return;
|
|
|
|
|
|
- AbstractSelect2.prototype.ensureHighlightVisible = function () {
|
|
|
- var results = this.results, children, index, child, hb, rb, y, more;
|
|
|
+ this.dropdown.hide();
|
|
|
+ this.container.removeClass("select2-dropdown-open");
|
|
|
+ this.results.empty();
|
|
|
+ this.clearSearch();
|
|
|
+ },
|
|
|
|
|
|
- children = results.children(".select2-result");
|
|
|
- index = this.highlight();
|
|
|
+ clearSearch: function () {
|
|
|
|
|
|
- if (index < 0) return;
|
|
|
+ },
|
|
|
|
|
|
- child = $(children[index]);
|
|
|
+ ensureHighlightVisible: function () {
|
|
|
+ var results = this.results, children, index, child, hb, rb, y, more;
|
|
|
|
|
|
- hb = child.offset().top + child.outerHeight();
|
|
|
+ children = results.children(".select2-result");
|
|
|
+ index = this.highlight();
|
|
|
|
|
|
- // 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();
|
|
|
- }
|
|
|
- }
|
|
|
+ if (index < 0) return;
|
|
|
|
|
|
- rb = results.offset().top + results.outerHeight();
|
|
|
- if (hb > rb) {
|
|
|
- results.scrollTop(results.scrollTop() + (hb - rb));
|
|
|
- }
|
|
|
- y = child.offset().top - results.offset().top;
|
|
|
+ child = $(children[index]);
|
|
|
|
|
|
- // make sure the top of the element is visible
|
|
|
- if (y < 0) {
|
|
|
- results.scrollTop(results.scrollTop() + y); // y is negative
|
|
|
- }
|
|
|
- };
|
|
|
+ hb = child.offset().top + child.outerHeight();
|
|
|
|
|
|
- AbstractSelect2.prototype.moveHighlight = function (delta) {
|
|
|
- var choices = this.results.children(".select2-result"),
|
|
|
- index = this.highlight();
|
|
|
+ // 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();
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- while (index > -1 && index < choices.length) {
|
|
|
- index += delta;
|
|
|
- if (!$(choices[index]).hasClass("select2-disabled")) {
|
|
|
- this.highlight(index);
|
|
|
- break;
|
|
|
+ rb = results.offset().top + results.outerHeight();
|
|
|
+ if (hb > rb) {
|
|
|
+ results.scrollTop(results.scrollTop() + (hb - rb));
|
|
|
}
|
|
|
- }
|
|
|
- };
|
|
|
+ y = child.offset().top - results.offset().top;
|
|
|
|
|
|
- AbstractSelect2.prototype.highlight = function (index) {
|
|
|
- var choices = this.results.children(".select2-result");
|
|
|
+ // make sure the top of the element is visible
|
|
|
+ if (y < 0) {
|
|
|
+ results.scrollTop(results.scrollTop() + y); // y is negative
|
|
|
+ }
|
|
|
+ },
|
|
|
|
|
|
- if (arguments.length === 0) {
|
|
|
- return indexOf(choices.filter(".select2-highlighted")[0], choices.get());
|
|
|
- }
|
|
|
+ moveHighlight: function (delta) {
|
|
|
+ var choices = this.results.children(".select2-result"),
|
|
|
+ index = this.highlight();
|
|
|
|
|
|
- choices.removeClass("select2-highlighted");
|
|
|
+ while (index > -1 && index < choices.length) {
|
|
|
+ index += delta;
|
|
|
+ if (!$(choices[index]).hasClass("select2-disabled")) {
|
|
|
+ this.highlight(index);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
|
|
|
- if (index >= choices.length) index = choices.length - 1;
|
|
|
- if (index < 0) index = 0;
|
|
|
+ highlight: function (index) {
|
|
|
+ var choices = this.results.children(".select2-result");
|
|
|
|
|
|
- $(choices[index]).addClass("select2-highlighted");
|
|
|
- this.ensureHighlightVisible();
|
|
|
+ if (arguments.length === 0) {
|
|
|
+ return indexOf(choices.filter(".select2-highlighted")[0], choices.get());
|
|
|
+ }
|
|
|
|
|
|
- if (this.opened()) this.focusSearch();
|
|
|
- };
|
|
|
+ choices.removeClass("select2-highlighted");
|
|
|
|
|
|
- AbstractSelect2.prototype.highlightUnderEvent = function (event) {
|
|
|
- var el = $(event.target).closest(".select2-result");
|
|
|
- if (el.length > 0) {
|
|
|
- this.highlight(el.index());
|
|
|
- }
|
|
|
- };
|
|
|
+ if (index >= choices.length) index = choices.length - 1;
|
|
|
+ if (index < 0) index = 0;
|
|
|
|
|
|
- AbstractSelect2.prototype.loadMoreIfNeeded = function () {
|
|
|
- var results = this.results,
|
|
|
- more = results.find("li.select2-more-results"),
|
|
|
- below, // pixels the element is below the scroll fold, below==0 is when the element is starting to be visible
|
|
|
- offset = -1, // index of first element without data
|
|
|
- page = this.resultsPage + 1;
|
|
|
+ $(choices[index]).addClass("select2-highlighted");
|
|
|
+ this.ensureHighlightVisible();
|
|
|
|
|
|
- if (more.length === 0) return;
|
|
|
+ if (this.opened()) this.focusSearch();
|
|
|
+ },
|
|
|
|
|
|
- below = more.offset().top - results.offset().top - results.height();
|
|
|
+ highlightUnderEvent: function (event) {
|
|
|
+ var el = $(event.target).closest(".select2-result");
|
|
|
+ if (el.length > 0) {
|
|
|
+ this.highlight(el.index());
|
|
|
+ }
|
|
|
+ },
|
|
|
|
|
|
- if (below <= 0) {
|
|
|
- more.addClass("select2-active");
|
|
|
- this.opts.query({term: this.search.val(), page: page, callback: this.bind(function (data) {
|
|
|
- var parts = [], self = this;
|
|
|
- $(data.results).each(function () {
|
|
|
- parts.push("<li class='select2-result'>");
|
|
|
- parts.push(self.opts.formatResult(this));
|
|
|
- parts.push("</li>");
|
|
|
- });
|
|
|
- more.before(parts.join(""));
|
|
|
- results.find(".select2-result").each(function (i) {
|
|
|
- var e = $(this);
|
|
|
- if (e.data("select2-data") !== undefined) {
|
|
|
- offset = i;
|
|
|
+ loadMoreIfNeeded: function () {
|
|
|
+ var results = this.results,
|
|
|
+ more = results.find("li.select2-more-results"),
|
|
|
+ below, // pixels the element is below the scroll fold, below==0 is when the element is starting to be visible
|
|
|
+ offset = -1, // index of first element without data
|
|
|
+ page = this.resultsPage + 1;
|
|
|
+
|
|
|
+ if (more.length === 0) return;
|
|
|
+
|
|
|
+ below = more.offset().top - results.offset().top - results.height();
|
|
|
+
|
|
|
+ if (below <= 0) {
|
|
|
+ more.addClass("select2-active");
|
|
|
+ this.opts.query({term: this.search.val(), page: page, callback: this.bind(function (data) {
|
|
|
+ var parts = [], self = this;
|
|
|
+ $(data.results).each(function () {
|
|
|
+ parts.push("<li class='select2-result'>");
|
|
|
+ parts.push(self.opts.formatResult(this));
|
|
|
+ parts.push("</li>");
|
|
|
+ });
|
|
|
+ more.before(parts.join(""));
|
|
|
+ results.find(".select2-result").each(function (i) {
|
|
|
+ var e = $(this);
|
|
|
+ if (e.data("select2-data") !== undefined) {
|
|
|
+ offset = i;
|
|
|
+ } else {
|
|
|
+ e.data("select2-data", data.results[i - offset - 1]);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ if (data.more) {
|
|
|
+ more.removeClass("select2-active");
|
|
|
} else {
|
|
|
- e.data("select2-data", data.results[i - offset - 1]);
|
|
|
+ more.remove();
|
|
|
}
|
|
|
- });
|
|
|
- if (data.more) {
|
|
|
- more.removeClass("select2-active");
|
|
|
- } else {
|
|
|
- more.remove();
|
|
|
- }
|
|
|
- this.resultsPage = page;
|
|
|
- })});
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- /**
|
|
|
- * @param initial whether or not this is the call to this method right after the dropdown has been opened
|
|
|
- */
|
|
|
- AbstractSelect2.prototype.updateResults = function (initial) {
|
|
|
- var search = this.search, results = this.results, opts = this.opts;
|
|
|
+ this.resultsPage = page;
|
|
|
+ })});
|
|
|
+ }
|
|
|
+ },
|
|
|
|
|
|
- search.addClass("select2-active");
|
|
|
+ /**
|
|
|
+ * @param initial whether or not this is the call to this method right after the dropdown has been opened
|
|
|
+ */
|
|
|
+ updateResults: function (initial) {
|
|
|
+ var search = this.search, results = this.results, opts = this.opts;
|
|
|
|
|
|
- function render(html) {
|
|
|
- results.html(html);
|
|
|
- results.scrollTop(0);
|
|
|
- search.removeClass("select2-active");
|
|
|
- }
|
|
|
+ search.addClass("select2-active");
|
|
|
|
|
|
- if (search.val().length < opts.minimumInputLength) {
|
|
|
- render("<li class='select2-no-results'>" + opts.formatInputTooShort(search.val(), opts.minimumInputLength) + "</li>");
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- this.resultsPage = 1;
|
|
|
- opts.query({term: search.val(), page: this.resultsPage, callback: this.bind(function (data) {
|
|
|
- var parts = [], // html parts
|
|
|
- def; // default choice
|
|
|
-
|
|
|
- // create a default choice and prepend it to the list
|
|
|
- if (this.opts.createSearchChoice && search.val() !== "") {
|
|
|
- def = this.opts.createSearchChoice.call(null, search.val(), data.results);
|
|
|
- if (def !== undefined && def !== null && def.id !== undefined && def.id != null) {
|
|
|
- if ($(data.results).filter(
|
|
|
- function () {
|
|
|
- return equal(this.id, def.id);
|
|
|
- }).length === 0) {
|
|
|
- data.results.unshift(def);
|
|
|
- }
|
|
|
- }
|
|
|
+ function render(html) {
|
|
|
+ results.html(html);
|
|
|
+ results.scrollTop(0);
|
|
|
+ search.removeClass("select2-active");
|
|
|
}
|
|
|
|
|
|
- if (data.results.length === 0) {
|
|
|
- render("<li class='select2-no-results'>" + opts.formatNoMatches(search.val()) + "</li>");
|
|
|
+ if (search.val().length < opts.minimumInputLength) {
|
|
|
+ render("<li class='select2-no-results'>" + opts.formatInputTooShort(search.val(), opts.minimumInputLength) + "</li>");
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- $(data.results).each(function () {
|
|
|
- parts.push("<li class='select2-result'>");
|
|
|
- parts.push(opts.formatResult(this));
|
|
|
- parts.push("</li>");
|
|
|
- });
|
|
|
+ this.resultsPage = 1;
|
|
|
+ opts.query({term: search.val(), page: this.resultsPage, callback: this.bind(function (data) {
|
|
|
+ var parts = [], // html parts
|
|
|
+ def; // default choice
|
|
|
+
|
|
|
+ // create a default choice and prepend it to the list
|
|
|
+ if (this.opts.createSearchChoice && search.val() !== "") {
|
|
|
+ def = this.opts.createSearchChoice.call(null, search.val(), data.results);
|
|
|
+ if (def !== undefined && def !== null && def.id !== undefined && def.id != null) {
|
|
|
+ if ($(data.results).filter(
|
|
|
+ function () {
|
|
|
+ return equal(this.id, def.id);
|
|
|
+ }).length === 0) {
|
|
|
+ data.results.unshift(def);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- if (data.more === true) {
|
|
|
- parts.push("<li class='select2-more-results'>Loading more results...</li>");
|
|
|
- }
|
|
|
+ if (data.results.length === 0) {
|
|
|
+ render("<li class='select2-no-results'>" + opts.formatNoMatches(search.val()) + "</li>");
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
- render(parts.join(""));
|
|
|
- results.children(".select2-result").each(function (i) {
|
|
|
- var d = data.results[i];
|
|
|
- $(this).data("select2-data", d);
|
|
|
- });
|
|
|
- this.postprocessResults(data, initial);
|
|
|
- })});
|
|
|
- };
|
|
|
+ $(data.results).each(function () {
|
|
|
+ parts.push("<li class='select2-result'>");
|
|
|
+ parts.push(opts.formatResult(this));
|
|
|
+ parts.push("</li>");
|
|
|
+ });
|
|
|
|
|
|
- AbstractSelect2.prototype.cancel = function () {
|
|
|
- this.close();
|
|
|
- };
|
|
|
+ if (data.more === true) {
|
|
|
+ parts.push("<li class='select2-more-results'>Loading more results...</li>");
|
|
|
+ }
|
|
|
+
|
|
|
+ render(parts.join(""));
|
|
|
+ results.children(".select2-result").each(function (i) {
|
|
|
+ var d = data.results[i];
|
|
|
+ $(this).data("select2-data", d);
|
|
|
+ });
|
|
|
+ this.postprocessResults(data, initial);
|
|
|
+ })});
|
|
|
+ },
|
|
|
|
|
|
- AbstractSelect2.prototype.blur = function () {
|
|
|
- /* we do this in a timeout so that current event processing can complete before this code is executed.
|
|
|
- this allows tab index to be preserved even if this code blurs the textfield */
|
|
|
- window.setTimeout(this.bind(function () {
|
|
|
+ cancel: function () {
|
|
|
this.close();
|
|
|
- this.container.removeClass("select2-container-active");
|
|
|
- this.clearSearch();
|
|
|
- this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus");
|
|
|
- this.search.blur();
|
|
|
- }), 10);
|
|
|
- };
|
|
|
+ },
|
|
|
|
|
|
- AbstractSelect2.prototype.focusSearch = function () {
|
|
|
- /* we do this in a timeout so that current event processing can complete before this code is executed.
|
|
|
- this makes sure the search field is focussed even if the current event would blur it */
|
|
|
- window.setTimeout(this.bind(function () {
|
|
|
- this.search.focus();
|
|
|
- }), 10);
|
|
|
- };
|
|
|
+ blur: function () {
|
|
|
+ /* we do this in a timeout so that current event processing can complete before this code is executed.
|
|
|
+ this allows tab index to be preserved even if this code blurs the textfield */
|
|
|
+ window.setTimeout(this.bind(function () {
|
|
|
+ this.close();
|
|
|
+ this.container.removeClass("select2-container-active");
|
|
|
+ this.clearSearch();
|
|
|
+ this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus");
|
|
|
+ this.search.blur();
|
|
|
+ }), 10);
|
|
|
+ },
|
|
|
|
|
|
- AbstractSelect2.prototype.selectHighlighted = function () {
|
|
|
- var data = this.results.find(".select2-highlighted:not(.select2-disabled)").data("select2-data");
|
|
|
- if (data) {
|
|
|
- this.onSelect(data);
|
|
|
- }
|
|
|
- };
|
|
|
+ focusSearch: function () {
|
|
|
+ /* we do this in a timeout so that current event processing can complete before this code is executed.
|
|
|
+ this makes sure the search field is focussed even if the current event would blur it */
|
|
|
+ window.setTimeout(this.bind(function () {
|
|
|
+ this.search.focus();
|
|
|
+ }), 10);
|
|
|
+ },
|
|
|
|
|
|
- AbstractSelect2.prototype.getPlaceholder = function () {
|
|
|
- var placeholder = this.opts.element.data("placeholder");
|
|
|
- if (placeholder !== undefined) return placeholder;
|
|
|
- return this.opts.placeholder;
|
|
|
- };
|
|
|
+ selectHighlighted: function () {
|
|
|
+ var data = this.results.find(".select2-highlighted:not(.select2-disabled)").data("select2-data");
|
|
|
+ if (data) {
|
|
|
+ this.onSelect(data);
|
|
|
+ }
|
|
|
+ },
|
|
|
|
|
|
- /**
|
|
|
- * Get the desired width for the container element. This is
|
|
|
- * derived first from option `width` passed to select2, then
|
|
|
- * the inline 'style' on the original element, and finally
|
|
|
- * falls back to the jQuery calculated element width.
|
|
|
- *
|
|
|
- * @returns The width string (with units) for the container.
|
|
|
- */
|
|
|
- AbstractSelect2.prototype.getContainerWidth = function () {
|
|
|
- if (this.opts.width !== undefined)
|
|
|
- return this.opts.width;
|
|
|
-
|
|
|
- var style = this.opts.element.attr('style');
|
|
|
- if (style !== undefined) {
|
|
|
- var attrs = style.split(';');
|
|
|
- for (var i = 0; i < attrs.length; i++) {
|
|
|
- var matches = attrs[i].replace(/\s/g, '')
|
|
|
- .match(/width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/);
|
|
|
- if (matches != null && matches.length >= 1)
|
|
|
- return matches[1];
|
|
|
+ getPlaceholder: function () {
|
|
|
+ var placeholder = this.opts.element.data("placeholder");
|
|
|
+ if (placeholder !== undefined) return placeholder;
|
|
|
+ return this.opts.placeholder;
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Get the desired width for the container element. This is
|
|
|
+ * derived first from option `width` passed to select2, then
|
|
|
+ * the inline 'style' on the original element, and finally
|
|
|
+ * falls back to the jQuery calculated element width.
|
|
|
+ *
|
|
|
+ * @returns The width string (with units) for the container.
|
|
|
+ */
|
|
|
+ getContainerWidth: function () {
|
|
|
+ if (this.opts.width !== undefined)
|
|
|
+ return this.opts.width;
|
|
|
+
|
|
|
+ var style = this.opts.element.attr('style');
|
|
|
+ if (style !== undefined) {
|
|
|
+ var attrs = style.split(';');
|
|
|
+ for (var i = 0; i < attrs.length; i++) {
|
|
|
+ var matches = attrs[i].replace(/\s/g, '')
|
|
|
+ .match(/width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/);
|
|
|
+ if (matches != null && matches.length >= 1)
|
|
|
+ return matches[1];
|
|
|
+ }
|
|
|
}
|
|
|
+ return this.opts.element.width() + 'px';
|
|
|
}
|
|
|
- return this.opts.element.width() + 'px';
|
|
|
- };
|
|
|
+ });
|
|
|
|
|
|
- function SingleSelect2() {
|
|
|
- }
|
|
|
+ SingleSelect2 = clazz(AbstractSelect2, {
|
|
|
+
|
|
|
+ createContainer: function () {
|
|
|
+ return $("<div></div>", {
|
|
|
+ "class": "select2-container",
|
|
|
+ "style": "width: " + this.getContainerWidth()
|
|
|
+ }).html([
|
|
|
+ " <a href='javascript:void(0)' class='select2-choice'>",
|
|
|
+ " <span></span><abbr class='select2-search-choice-close' style='display:none;'></abbr>",
|
|
|
+ " <div><b></b></div>" ,
|
|
|
+ "</a>",
|
|
|
+ " <div class='select2-drop' style='display:none;'>" ,
|
|
|
+ " <div class='select2-search'>" ,
|
|
|
+ " <input type='text' autocomplete='off'/>" ,
|
|
|
+ " </div>" ,
|
|
|
+ " <ul class='select2-results'>" ,
|
|
|
+ " </ul>" ,
|
|
|
+ "</div>"].join(""));
|
|
|
+ },
|
|
|
|
|
|
- SingleSelect2.prototype = new AbstractSelect2();
|
|
|
- SingleSelect2.prototype.constructor = SingleSelect2;
|
|
|
- SingleSelect2.prototype.parent = AbstractSelect2.prototype;
|
|
|
-
|
|
|
- SingleSelect2.prototype.createContainer = function () {
|
|
|
- return $("<div></div>", {
|
|
|
- "class": "select2-container",
|
|
|
- "style": "width: " + this.getContainerWidth()
|
|
|
- }).html([
|
|
|
- " <a href='javascript:void(0)' class='select2-choice'>",
|
|
|
- " <span></span><abbr class='select2-search-choice-close' style='display:none;'></abbr>",
|
|
|
- " <div><b></b></div>" ,
|
|
|
- "</a>",
|
|
|
- " <div class='select2-drop' style='display:none;'>" ,
|
|
|
- " <div class='select2-search'>" ,
|
|
|
- " <input type='text' autocomplete='off'/>" ,
|
|
|
- " </div>" ,
|
|
|
- " <ul class='select2-results'>" ,
|
|
|
- " </ul>" ,
|
|
|
- "</div>"].join(""));
|
|
|
- };
|
|
|
+ open: function () {
|
|
|
+
|
|
|
+ if (this.opened()) return;
|
|
|
|
|
|
- SingleSelect2.prototype.open = function () {
|
|
|
+ this.parent.open.apply(this, arguments);
|
|
|
|
|
|
- if (this.opened()) return;
|
|
|
+ },
|
|
|
|
|
|
- this.parent.open.apply(this, arguments);
|
|
|
+ close: function () {
|
|
|
+ if (!this.opened()) return;
|
|
|
+ this.parent.close.apply(this, arguments);
|
|
|
+ },
|
|
|
|
|
|
- };
|
|
|
+ cancel: function () {
|
|
|
+ this.parent.cancel.apply(this, arguments);
|
|
|
+ this.selection.focus();
|
|
|
+ },
|
|
|
|
|
|
- SingleSelect2.prototype.close = function () {
|
|
|
- if (!this.opened()) return;
|
|
|
- this.parent.close.apply(this, arguments);
|
|
|
- };
|
|
|
+ initContainer: function () {
|
|
|
|
|
|
- SingleSelect2.prototype.cancel = function () {
|
|
|
- this.parent.cancel.apply(this, arguments);
|
|
|
- this.selection.focus();
|
|
|
- };
|
|
|
+ var selection, container = this.container, clickingInside = false,
|
|
|
+ selector = ".select2-choice", selected;
|
|
|
+
|
|
|
+ this.selection = selection = container.find(selector);
|
|
|
|
|
|
- SingleSelect2.prototype.initContainer = function () {
|
|
|
+ this.search.bind("keydown", this.bind(function (e) {
|
|
|
+ switch (e.which) {
|
|
|
+ case KEY.UP:
|
|
|
+ case KEY.DOWN:
|
|
|
+ this.moveHighlight((e.which === KEY.UP) ? -1 : 1);
|
|
|
+ killEvent(e);
|
|
|
+ return;
|
|
|
+ case KEY.TAB:
|
|
|
+ case KEY.ENTER:
|
|
|
+ this.selectHighlighted();
|
|
|
+ killEvent(e);
|
|
|
+ return;
|
|
|
+ case KEY.ESC:
|
|
|
+ this.cancel(e);
|
|
|
+ e.preventDefault();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }));
|
|
|
|
|
|
- var selection, container = this.container, clickingInside = false,
|
|
|
- selector = ".select2-choice", selected;
|
|
|
+ container.delegate(selector, "click", this.bind(function (e) {
|
|
|
+ clickingInside = true;
|
|
|
|
|
|
- this.selection = selection = container.find(selector);
|
|
|
+ if (this.opened()) {
|
|
|
+ this.close();
|
|
|
+ selection.focus();
|
|
|
+ } else {
|
|
|
+ this.open();
|
|
|
+ }
|
|
|
+ e.preventDefault();
|
|
|
|
|
|
- this.search.bind("keydown", this.bind(function (e) {
|
|
|
- switch (e.which) {
|
|
|
- case KEY.UP:
|
|
|
- case KEY.DOWN:
|
|
|
- this.moveHighlight((e.which === KEY.UP) ? -1 : 1);
|
|
|
- killEvent(e);
|
|
|
- return;
|
|
|
- case KEY.TAB:
|
|
|
- case KEY.ENTER:
|
|
|
- this.selectHighlighted();
|
|
|
+ clickingInside = false;
|
|
|
+ }));
|
|
|
+ container.delegate(selector, "keydown", this.bind(function (e) {
|
|
|
+ if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.ESC) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.open();
|
|
|
+ if (e.which === KEY.PAGE_UP || e.which === KEY.PAGE_DOWN || e.which === KEY.SPACE) {
|
|
|
+ // prevent the page from scrolling
|
|
|
+ killEvent(e);
|
|
|
+ }
|
|
|
+ if (e.which === KEY.ENTER) {
|
|
|
+ // do not propagate the event otherwise we open, and propagate enter which closes
|
|
|
+ killEvent(e);
|
|
|
+ }
|
|
|
+ }));
|
|
|
+ container.delegate(selector, "focus", function () { container.addClass("select2-container-active"); });
|
|
|
+ container.delegate(selector, "blur", this.bind(function () {
|
|
|
+ if (clickingInside) return;
|
|
|
+ if (!this.opened()) this.blur();
|
|
|
+ }));
|
|
|
+
|
|
|
+ selection.delegate("abbr", "click", this.bind(function (e) {
|
|
|
+ this.val("");
|
|
|
killEvent(e);
|
|
|
- return;
|
|
|
- case KEY.ESC:
|
|
|
- this.cancel(e);
|
|
|
- e.preventDefault();
|
|
|
- return;
|
|
|
- }
|
|
|
- }));
|
|
|
+ this.close();
|
|
|
+ this.triggerChange();
|
|
|
+ }));
|
|
|
|
|
|
- container.delegate(selector, "click", this.bind(function (e) {
|
|
|
- clickingInside = true;
|
|
|
+ this.setPlaceholder();
|
|
|
+ },
|
|
|
|
|
|
- if (this.opened()) {
|
|
|
- this.close();
|
|
|
- selection.focus();
|
|
|
+ /**
|
|
|
+ * Sets selection based on source element's value
|
|
|
+ */
|
|
|
+ initSelection: function () {
|
|
|
+ var selected;
|
|
|
+ if (this.opts.element.val() === "") {
|
|
|
+ this.updateSelection({id: "", text: ""});
|
|
|
} else {
|
|
|
- this.open();
|
|
|
+ selected = this.opts.initSelection.call(null, this.opts.element);
|
|
|
+ if (selected !== undefined && selected !== null) {
|
|
|
+ this.updateSelection(selected);
|
|
|
+ }
|
|
|
}
|
|
|
- e.preventDefault();
|
|
|
|
|
|
- clickingInside = false;
|
|
|
- }));
|
|
|
- container.delegate(selector, "keydown", this.bind(function (e) {
|
|
|
- if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.ESC) {
|
|
|
- return;
|
|
|
- }
|
|
|
- this.open();
|
|
|
- if (e.which === KEY.PAGE_UP || e.which === KEY.PAGE_DOWN || e.which === KEY.SPACE) {
|
|
|
- // prevent the page from scrolling
|
|
|
- killEvent(e);
|
|
|
- }
|
|
|
- if (e.which === KEY.ENTER) {
|
|
|
- // do not propagate the event otherwise we open, and propagate enter which closes
|
|
|
- killEvent(e);
|
|
|
- }
|
|
|
- }));
|
|
|
- container.delegate(selector, "focus", function () { container.addClass("select2-container-active"); });
|
|
|
- container.delegate(selector, "blur", this.bind(function () {
|
|
|
- if (clickingInside) return;
|
|
|
- if (!this.opened()) this.blur();
|
|
|
- }));
|
|
|
-
|
|
|
- selection.delegate("abbr", "click", this.bind(function (e) {
|
|
|
- this.val("");
|
|
|
- killEvent(e);
|
|
|
this.close();
|
|
|
- this.triggerChange();
|
|
|
- }));
|
|
|
+ this.setPlaceholder();
|
|
|
+ },
|
|
|
|
|
|
- this.setPlaceholder();
|
|
|
- };
|
|
|
+ prepareOpts: function () {
|
|
|
+ var opts = this.parent.prepareOpts.apply(this, arguments);
|
|
|
|
|
|
- /**
|
|
|
- * Sets selection based on source element's value
|
|
|
- */
|
|
|
- SingleSelect2.prototype.initSelection = function () {
|
|
|
- var selected;
|
|
|
- if (this.opts.element.val() === "") {
|
|
|
- this.updateSelection({id: "", text: ""});
|
|
|
- } else {
|
|
|
- selected = this.opts.initSelection.call(null, this.opts.element);
|
|
|
- if (selected !== undefined && selected !== null) {
|
|
|
- this.updateSelection(selected);
|
|
|
+ if (opts.element.get(0).tagName.toLowerCase() === "select") {
|
|
|
+ // install sthe selection initializer
|
|
|
+ this.opts.initSelection = function (element) {
|
|
|
+ var selected = element.find(":selected");
|
|
|
+ // a single select box always has a value, no need to null check 'selected'
|
|
|
+ return {id: selected.attr("value"), text: selected.text()};
|
|
|
+ };
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
- this.close();
|
|
|
- this.setPlaceholder();
|
|
|
- };
|
|
|
-
|
|
|
- SingleSelect2.prototype.prepareOpts = function () {
|
|
|
- var opts = this.parent.prepareOpts.apply(this, arguments);
|
|
|
|
|
|
- if (opts.element.get(0).tagName.toLowerCase() === "select") {
|
|
|
- // install sthe selection initializer
|
|
|
- this.opts.initSelection = function (element) {
|
|
|
- var selected = element.find(":selected");
|
|
|
- // a single select box always has a value, no need to null check 'selected'
|
|
|
- return {id: selected.attr("value"), text: selected.text()};
|
|
|
- };
|
|
|
- }
|
|
|
+ return opts;
|
|
|
+ },
|
|
|
|
|
|
- return opts;
|
|
|
- };
|
|
|
+ setPlaceholder: function () {
|
|
|
+ var placeholder = this.getPlaceholder();
|
|
|
|
|
|
- SingleSelect2.prototype.setPlaceholder = function () {
|
|
|
- var placeholder = this.getPlaceholder();
|
|
|
+ if (this.opts.element.val() === "" && placeholder !== undefined) {
|
|
|
|
|
|
- if (this.opts.element.val() === "" && placeholder !== undefined) {
|
|
|
+ // check for a first blank option if attached to a select
|
|
|
+ if (this.select && this.select.find("option:first").text() !== "") return;
|
|
|
|
|
|
- // check for a first blank option if attached to a select
|
|
|
- if (this.select && this.select.find("option:first").text() !== "") return;
|
|
|
+ if (typeof(placeholder) === "object") {
|
|
|
+ this.updateSelection(placeholder);
|
|
|
+ } else {
|
|
|
+ this.selection.find("span").html(placeholder);
|
|
|
+ }
|
|
|
+ this.selection.addClass("select2-default");
|
|
|
|
|
|
- if (typeof(placeholder) === "object") {
|
|
|
- this.updateSelection(placeholder);
|
|
|
- } else {
|
|
|
- this.selection.find("span").html(placeholder);
|
|
|
+ this.selection.find("abbr").hide();
|
|
|
}
|
|
|
- this.selection.addClass("select2-default");
|
|
|
-
|
|
|
- this.selection.find("abbr").hide();
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- SingleSelect2.prototype.postprocessResults = function (data, initial) {
|
|
|
- var selected = 0, self = this;
|
|
|
-
|
|
|
- // find the selected element in the result list
|
|
|
+ },
|
|
|
|
|
|
- this.results.find(".select2-result").each(function (i) {
|
|
|
- if (equal($(this).data("select2-data").id, self.opts.element.val())) {
|
|
|
- selected = i;
|
|
|
- return false;
|
|
|
- }
|
|
|
- });
|
|
|
+ postprocessResults: function (data, initial) {
|
|
|
+ var selected = 0, self = this;
|
|
|
|
|
|
- // and highlight it
|
|
|
+ // find the selected element in the result list
|
|
|
|
|
|
- this.highlight(selected);
|
|
|
+ this.results.find(".select2-result").each(function (i) {
|
|
|
+ if (equal($(this).data("select2-data").id, self.opts.element.val())) {
|
|
|
+ selected = i;
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ });
|
|
|
|
|
|
- // hide the search box if this is the first we got the results and there are a few of them
|
|
|
+ // and highlight it
|
|
|
|
|
|
- if (initial === true) {
|
|
|
- this.search.parent().toggle(data.results.length >= this.opts.minimumResultsForSearch);
|
|
|
- }
|
|
|
+ this.highlight(selected);
|
|
|
|
|
|
- };
|
|
|
+ // hide the search box if this is the first we got the results and there are a few of them
|
|
|
|
|
|
- SingleSelect2.prototype.onSelect = function (data) {
|
|
|
- var old = this.opts.element.val();
|
|
|
+ if (initial === true) {
|
|
|
+ this.search.parent().toggle(data.results.length >= this.opts.minimumResultsForSearch);
|
|
|
+ }
|
|
|
|
|
|
- this.opts.element.val(data.id);
|
|
|
- this.updateSelection(data);
|
|
|
- this.close();
|
|
|
- this.selection.focus();
|
|
|
+ },
|
|
|
|
|
|
- if (!equal(old, data.id)) { this.triggerChange(); }
|
|
|
- };
|
|
|
+ onSelect: function (data) {
|
|
|
+ var old = this.opts.element.val();
|
|
|
|
|
|
- SingleSelect2.prototype.updateSelection = function (data) {
|
|
|
- this.selection
|
|
|
- .find("span")
|
|
|
- .html(this.opts.formatSelection(data));
|
|
|
+ this.opts.element.val(data.id);
|
|
|
+ this.updateSelection(data);
|
|
|
+ this.close();
|
|
|
+ this.selection.focus();
|
|
|
|
|
|
- this.selection.removeClass("select2-default");
|
|
|
+ if (!equal(old, data.id)) { this.triggerChange(); }
|
|
|
+ },
|
|
|
|
|
|
- if (this.opts.allowClear && this.getPlaceholder() !== undefined) {
|
|
|
- this.selection.find("abbr").show();
|
|
|
- }
|
|
|
- };
|
|
|
+ updateSelection: function (data) {
|
|
|
+ this.selection
|
|
|
+ .find("span")
|
|
|
+ .html(this.opts.formatSelection(data));
|
|
|
|
|
|
- SingleSelect2.prototype.val = function () {
|
|
|
- var val, data = null;
|
|
|
+ this.selection.removeClass("select2-default");
|
|
|
|
|
|
- if (arguments.length === 0) {
|
|
|
- return this.opts.element.val();
|
|
|
- }
|
|
|
+ if (this.opts.allowClear && this.getPlaceholder() !== undefined) {
|
|
|
+ this.selection.find("abbr").show();
|
|
|
+ }
|
|
|
+ },
|
|
|
|
|
|
- val = arguments[0];
|
|
|
+ val: function () {
|
|
|
+ var val, data = null;
|
|
|
|
|
|
- if (this.select) {
|
|
|
- // val is an id
|
|
|
- this.select
|
|
|
- .val(val)
|
|
|
- .find(":selected").each(function () {
|
|
|
- data = {id: $(this).attr("value"), text: $(this).text()};
|
|
|
- return false;
|
|
|
- });
|
|
|
- this.updateSelection(data);
|
|
|
- } else {
|
|
|
- // val is an object
|
|
|
- this.opts.element.val((val === null) ? "" : val.id);
|
|
|
- this.updateSelection(val);
|
|
|
- }
|
|
|
- this.setPlaceholder();
|
|
|
+ if (arguments.length === 0) {
|
|
|
+ return this.opts.element.val();
|
|
|
+ }
|
|
|
|
|
|
- };
|
|
|
+ val = arguments[0];
|
|
|
|
|
|
- SingleSelect2.prototype.clearSearch = function () {
|
|
|
- this.search.val("");
|
|
|
- };
|
|
|
+ if (this.select) {
|
|
|
+ // val is an id
|
|
|
+ this.select
|
|
|
+ .val(val)
|
|
|
+ .find(":selected").each(function () {
|
|
|
+ data = {id: $(this).attr("value"), text: $(this).text()};
|
|
|
+ return false;
|
|
|
+ });
|
|
|
+ this.updateSelection(data);
|
|
|
+ } else {
|
|
|
+ // val is an object
|
|
|
+ this.opts.element.val((val === null) ? "" : val.id);
|
|
|
+ this.updateSelection(val);
|
|
|
+ }
|
|
|
+ this.setPlaceholder();
|
|
|
|
|
|
- function MultiSelect2(opts) {
|
|
|
+ },
|
|
|
|
|
|
- }
|
|
|
+ clearSearch: function () {
|
|
|
+ this.search.val("");
|
|
|
+ }
|
|
|
+ });
|
|
|
|
|
|
- MultiSelect2.prototype = new AbstractSelect2();
|
|
|
- MultiSelect2.prototype.constructor = AbstractSelect2;
|
|
|
- MultiSelect2.prototype.parent = AbstractSelect2.prototype;
|
|
|
-
|
|
|
- MultiSelect2.prototype.createContainer = function () {
|
|
|
- return $("<div></div>", {
|
|
|
- "class": "select2-container select2-container-multi",
|
|
|
- "style": "width: " + this.getContainerWidth()
|
|
|
- }).html([
|
|
|
- " <ul class='select2-choices'>",
|
|
|
- //"<li class='select2-search-choice'><span>California</span><a href="javascript:void(0)" class="select2-search-choice-close"></a></li>" ,
|
|
|
- " <li class='select2-search-field'>" ,
|
|
|
- " <input type='text' autocomplete='off' style='width: 25px;'>" ,
|
|
|
- " </li>" ,
|
|
|
- "</ul>" ,
|
|
|
- "<div class='select2-drop' style='display:none;'>" ,
|
|
|
- " <ul class='select2-results'>" ,
|
|
|
- " </ul>" ,
|
|
|
- "</div>"].join(""));
|
|
|
- };
|
|
|
+ MultiSelect2 = clazz(AbstractSelect2, {
|
|
|
+
|
|
|
+ createContainer: function () {
|
|
|
+ return $("<div></div>", {
|
|
|
+ "class": "select2-container select2-container-multi",
|
|
|
+ "style": "width: " + this.getContainerWidth()
|
|
|
+ }).html([
|
|
|
+ " <ul class='select2-choices'>",
|
|
|
+ //"<li class='select2-search-choice'><span>California</span><a href="javascript:void(0)" class="select2-search-choice-close"></a></li>" ,
|
|
|
+ " <li class='select2-search-field'>" ,
|
|
|
+ " <input type='text' autocomplete='off' style='width: 25px;'>" ,
|
|
|
+ " </li>" ,
|
|
|
+ "</ul>" ,
|
|
|
+ "<div class='select2-drop' style='display:none;'>" ,
|
|
|
+ " <ul class='select2-results'>" ,
|
|
|
+ " </ul>" ,
|
|
|
+ "</div>"].join(""));
|
|
|
+ },
|
|
|
|
|
|
- MultiSelect2.prototype.prepareOpts = function () {
|
|
|
- var opts = this.parent.prepareOpts.apply(this, arguments);
|
|
|
+ prepareOpts: function () {
|
|
|
+ var opts = this.parent.prepareOpts.apply(this, arguments);
|
|
|
+
|
|
|
+ if (opts.element.get(0).tagName.toLowerCase() === "select") {
|
|
|
+ // install sthe selection initializer
|
|
|
+ this.opts.initSelection = function (element) {
|
|
|
+ var data = [];
|
|
|
+ element.find(":selected").each(function () {
|
|
|
+ data.push({id: $(this).attr("value"), text: $(this).text()});
|
|
|
+ });
|
|
|
+ return data;
|
|
|
+ };
|
|
|
+ }
|
|
|
|
|
|
- if (opts.element.get(0).tagName.toLowerCase() === "select") {
|
|
|
- // install sthe selection initializer
|
|
|
- this.opts.initSelection = function (element) {
|
|
|
- var data = [];
|
|
|
- element.find(":selected").each(function () {
|
|
|
- data.push({id: $(this).attr("value"), text: $(this).text()});
|
|
|
- });
|
|
|
- return data;
|
|
|
- };
|
|
|
- }
|
|
|
+ return opts;
|
|
|
+ },
|
|
|
|
|
|
- return opts;
|
|
|
- };
|
|
|
+ initContainer: function () {
|
|
|
|
|
|
- MultiSelect2.prototype.initContainer = function () {
|
|
|
+ var selector = ".select2-choices", selection, data;
|
|
|
|
|
|
- var selector = ".select2-choices", selection, data;
|
|
|
+ this.searchContainer = this.container.find(".select2-search-field");
|
|
|
+ this.selection = selection = this.container.find(selector);
|
|
|
|
|
|
- this.searchContainer = this.container.find(".select2-search-field");
|
|
|
- this.selection = selection = this.container.find(selector);
|
|
|
+ this.search.bind("keydown", this.bind(function (e) {
|
|
|
+ if (e.which === KEY.BACKSPACE && this.search.val() === "") {
|
|
|
+ this.close();
|
|
|
|
|
|
- this.search.bind("keydown", this.bind(function (e) {
|
|
|
- if (e.which === KEY.BACKSPACE && this.search.val() === "") {
|
|
|
- this.close();
|
|
|
+ var choices,
|
|
|
+ selected = this.selection.find(".select2-search-choice-focus");
|
|
|
+ if (selected.length > 0) {
|
|
|
+ this.unselect(selected.first());
|
|
|
+ this.search.width(10);
|
|
|
+ killEvent(e);
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
- var choices,
|
|
|
- selected = this.selection.find(".select2-search-choice-focus");
|
|
|
- if (selected.length > 0) {
|
|
|
- this.unselect(selected.first());
|
|
|
- this.search.width(10);
|
|
|
- killEvent(e);
|
|
|
- return;
|
|
|
+ choices = this.selection.find(".select2-search-choice");
|
|
|
+ if (choices.length > 0) {
|
|
|
+ choices.last().addClass("select2-search-choice-focus");
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus");
|
|
|
}
|
|
|
|
|
|
- choices = this.selection.find(".select2-search-choice");
|
|
|
- if (choices.length > 0) {
|
|
|
- choices.last().addClass("select2-search-choice-focus");
|
|
|
+ if (this.opened()) {
|
|
|
+ switch (e.which) {
|
|
|
+ case KEY.UP:
|
|
|
+ case KEY.DOWN:
|
|
|
+ this.moveHighlight((e.which === KEY.UP) ? -1 : 1);
|
|
|
+ killEvent(e);
|
|
|
+ return;
|
|
|
+ case KEY.ENTER:
|
|
|
+ this.selectHighlighted();
|
|
|
+ killEvent(e);
|
|
|
+ return;
|
|
|
+ case KEY.ESC:
|
|
|
+ this.cancel(e);
|
|
|
+ e.preventDefault();
|
|
|
+ return;
|
|
|
+ }
|
|
|
}
|
|
|
- } else {
|
|
|
- this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus");
|
|
|
- }
|
|
|
|
|
|
- if (this.opened()) {
|
|
|
- switch (e.which) {
|
|
|
- case KEY.UP:
|
|
|
- case KEY.DOWN:
|
|
|
- this.moveHighlight((e.which === KEY.UP) ? -1 : 1);
|
|
|
- killEvent(e);
|
|
|
- return;
|
|
|
- case KEY.ENTER:
|
|
|
- this.selectHighlighted();
|
|
|
- killEvent(e);
|
|
|
- return;
|
|
|
- case KEY.ESC:
|
|
|
- this.cancel(e);
|
|
|
- e.preventDefault();
|
|
|
+ if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.BACKSPACE || e.which === KEY.ESC) {
|
|
|
return;
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.BACKSPACE || e.which === KEY.ESC) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- this.open();
|
|
|
+ this.open();
|
|
|
|
|
|
- if (e.which === KEY.PAGE_UP || e.which === KEY.PAGE_DOWN) {
|
|
|
- // prevent the page from scrolling
|
|
|
- killEvent(e);
|
|
|
- }
|
|
|
- }));
|
|
|
+ if (e.which === KEY.PAGE_UP || e.which === KEY.PAGE_DOWN) {
|
|
|
+ // prevent the page from scrolling
|
|
|
+ killEvent(e);
|
|
|
+ }
|
|
|
+ }));
|
|
|
|
|
|
- this.search.bind("keyup", this.bind(this.resizeSearch));
|
|
|
+ this.search.bind("keyup", this.bind(this.resizeSearch));
|
|
|
|
|
|
- this.container.delegate(selector, "click", this.bind(function (e) {
|
|
|
- this.open();
|
|
|
- this.focusSearch();
|
|
|
- e.preventDefault();
|
|
|
- }));
|
|
|
+ this.container.delegate(selector, "click", this.bind(function (e) {
|
|
|
+ this.open();
|
|
|
+ this.focusSearch();
|
|
|
+ e.preventDefault();
|
|
|
+ }));
|
|
|
|
|
|
- this.container.delegate(selector, "focus", this.bind(function () {
|
|
|
- this.container.addClass("select2-container-active");
|
|
|
- this.clearPlaceholder();
|
|
|
- }));
|
|
|
+ this.container.delegate(selector, "focus", this.bind(function () {
|
|
|
+ this.container.addClass("select2-container-active");
|
|
|
+ this.clearPlaceholder();
|
|
|
+ }));
|
|
|
|
|
|
- // set the placeholder if necessary
|
|
|
- this.clearSearch();
|
|
|
- };
|
|
|
+ // set the placeholder if necessary
|
|
|
+ this.clearSearch();
|
|
|
+ },
|
|
|
|
|
|
- MultiSelect2.prototype.initSelection = function () {
|
|
|
- var data;
|
|
|
- if (this.opts.element.val() === "") {
|
|
|
- this.updateSelection([]);
|
|
|
- }
|
|
|
- if (this.select || this.opts.element.val() !== "") {
|
|
|
- data = this.opts.initSelection.call(null, this.opts.element);
|
|
|
- if (data !== undefined && data != null) {
|
|
|
- this.updateSelection(data);
|
|
|
+ initSelection: function () {
|
|
|
+ var data;
|
|
|
+ if (this.opts.element.val() === "") {
|
|
|
+ this.updateSelection([]);
|
|
|
+ }
|
|
|
+ if (this.select || this.opts.element.val() !== "") {
|
|
|
+ data = this.opts.initSelection.call(null, this.opts.element);
|
|
|
+ if (data !== undefined && data != null) {
|
|
|
+ this.updateSelection(data);
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
- this.close();
|
|
|
|
|
|
- // set the placeholder if necessary
|
|
|
- this.clearSearch();
|
|
|
- };
|
|
|
+ this.close();
|
|
|
|
|
|
- MultiSelect2.prototype.clearSearch = function () {
|
|
|
- var placeholder = this.getPlaceholder();
|
|
|
+ // set the placeholder if necessary
|
|
|
+ this.clearSearch();
|
|
|
+ },
|
|
|
|
|
|
- this.search.val("").width(10);
|
|
|
+ clearSearch: function () {
|
|
|
+ var placeholder = this.getPlaceholder();
|
|
|
|
|
|
- if (placeholder !== undefined && this.getVal().length === 0) {
|
|
|
- this.search.val(placeholder).addClass("select2-default");
|
|
|
- this.resizeSearch();
|
|
|
- }
|
|
|
- };
|
|
|
+ this.search.val("").width(10);
|
|
|
|
|
|
- MultiSelect2.prototype.clearPlaceholder = function () {
|
|
|
- if (this.search.hasClass("select2-default")) {
|
|
|
- this.search.val("").removeClass("select2-default");
|
|
|
- }
|
|
|
- };
|
|
|
+ if (placeholder !== undefined && this.getVal().length === 0) {
|
|
|
+ this.search.val(placeholder).addClass("select2-default");
|
|
|
+ this.resizeSearch();
|
|
|
+ }
|
|
|
+ },
|
|
|
|
|
|
- MultiSelect2.prototype.open = function () {
|
|
|
- if (this.opened()) return;
|
|
|
- this.parent.open.apply(this, arguments);
|
|
|
- this.resizeSearch();
|
|
|
- this.focusSearch();
|
|
|
- };
|
|
|
+ clearPlaceholder: function () {
|
|
|
+ if (this.search.hasClass("select2-default")) {
|
|
|
+ this.search.val("").removeClass("select2-default");
|
|
|
+ }
|
|
|
+ },
|
|
|
|
|
|
- MultiSelect2.prototype.close = function () {
|
|
|
- if (!this.opened()) return;
|
|
|
- this.parent.close.apply(this, arguments);
|
|
|
- };
|
|
|
+ open: function () {
|
|
|
+ if (this.opened()) return;
|
|
|
+ this.parent.open.apply(this, arguments);
|
|
|
+ this.resizeSearch();
|
|
|
+ this.focusSearch();
|
|
|
+ },
|
|
|
|
|
|
- MultiSelect2.prototype.updateSelection = function (data) {
|
|
|
- var ids = [], filtered = [], self = this;
|
|
|
+ close: function () {
|
|
|
+ if (!this.opened()) return;
|
|
|
+ this.parent.close.apply(this, arguments);
|
|
|
+ },
|
|
|
|
|
|
- // filter out duplicates
|
|
|
- $(data).each(function () {
|
|
|
- if (indexOf(this.id, ids) < 0) {
|
|
|
- ids.push(this.id);
|
|
|
- filtered.push(this);
|
|
|
- }
|
|
|
- });
|
|
|
- data = filtered;
|
|
|
+ updateSelection: function (data) {
|
|
|
+ var ids = [], filtered = [], self = this;
|
|
|
|
|
|
- this.selection.find(".select2-search-choice").remove();
|
|
|
- $(data).each(function () {
|
|
|
- self.addSelectedChoice(this);
|
|
|
- });
|
|
|
- self.postprocessResults();
|
|
|
- this.alignDropdown();
|
|
|
- };
|
|
|
+ // filter out duplicates
|
|
|
+ $(data).each(function () {
|
|
|
+ if (indexOf(this.id, ids) < 0) {
|
|
|
+ ids.push(this.id);
|
|
|
+ filtered.push(this);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ data = filtered;
|
|
|
|
|
|
- MultiSelect2.prototype.onSelect = function (data) {
|
|
|
- this.addSelectedChoice(data);
|
|
|
- if (this.select) { this.postprocessResults(); }
|
|
|
- this.close();
|
|
|
- this.search.width(10);
|
|
|
+ this.selection.find(".select2-search-choice").remove();
|
|
|
+ $(data).each(function () {
|
|
|
+ self.addSelectedChoice(this);
|
|
|
+ });
|
|
|
+ self.postprocessResults();
|
|
|
+ this.alignDropdown();
|
|
|
+ },
|
|
|
|
|
|
- // 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();
|
|
|
+ onSelect: function (data) {
|
|
|
+ this.addSelectedChoice(data);
|
|
|
+ if (this.select) { this.postprocessResults(); }
|
|
|
+ this.close();
|
|
|
+ this.search.width(10);
|
|
|
|
|
|
- this.focusSearch();
|
|
|
- };
|
|
|
+ // 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();
|
|
|
|
|
|
- MultiSelect2.prototype.cancel = function () {
|
|
|
- this.close();
|
|
|
- this.focusSearch();
|
|
|
- };
|
|
|
+ this.focusSearch();
|
|
|
+ },
|
|
|
|
|
|
- MultiSelect2.prototype.addSelectedChoice = function (data) {
|
|
|
- var choice,
|
|
|
- id = data.id,
|
|
|
- parts,
|
|
|
- val = this.getVal();
|
|
|
-
|
|
|
- parts = ["<li class='select2-search-choice'>",
|
|
|
- this.opts.formatSelection(data),
|
|
|
- "<a href='javascript:void(0)' class='select2-search-choice-close' tabindex='-1'></a>",
|
|
|
- "</li>"
|
|
|
- ];
|
|
|
-
|
|
|
- choice = $(parts.join(""));
|
|
|
- choice.find("a")
|
|
|
- .bind("click dblclick", this.bind(function (e) {
|
|
|
- this.unselect($(e.target));
|
|
|
- this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus");
|
|
|
- killEvent(e);
|
|
|
+ cancel: function () {
|
|
|
this.close();
|
|
|
this.focusSearch();
|
|
|
- })).bind("focus", this.bind(function () {
|
|
|
- this.container.addClass("select2-container-active");
|
|
|
- }));
|
|
|
-
|
|
|
- choice.data("select2-data", data);
|
|
|
- choice.insertBefore(this.searchContainer);
|
|
|
+ },
|
|
|
|
|
|
- val.push(id);
|
|
|
- this.setVal(val);
|
|
|
- };
|
|
|
+ addSelectedChoice: function (data) {
|
|
|
+ var choice,
|
|
|
+ id = data.id,
|
|
|
+ parts,
|
|
|
+ val = this.getVal();
|
|
|
+
|
|
|
+ parts = ["<li class='select2-search-choice'>",
|
|
|
+ this.opts.formatSelection(data),
|
|
|
+ "<a href='javascript:void(0)' class='select2-search-choice-close' tabindex='-1'></a>",
|
|
|
+ "</li>"
|
|
|
+ ];
|
|
|
+
|
|
|
+ choice = $(parts.join(""));
|
|
|
+ choice.find("a")
|
|
|
+ .bind("click dblclick", this.bind(function (e) {
|
|
|
+ this.unselect($(e.target));
|
|
|
+ this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus");
|
|
|
+ killEvent(e);
|
|
|
+ this.close();
|
|
|
+ this.focusSearch();
|
|
|
+ })).bind("focus", this.bind(function () {
|
|
|
+ this.container.addClass("select2-container-active");
|
|
|
+ }));
|
|
|
|
|
|
- MultiSelect2.prototype.unselect = function (selected) {
|
|
|
- var val = this.getVal(),
|
|
|
- index;
|
|
|
+ choice.data("select2-data", data);
|
|
|
+ choice.insertBefore(this.searchContainer);
|
|
|
|
|
|
- selected = selected.closest(".select2-search-choice");
|
|
|
+ val.push(id);
|
|
|
+ this.setVal(val);
|
|
|
+ },
|
|
|
|
|
|
- if (selected.length === 0) {
|
|
|
- throw "Invalid argument: " + selected + ". Must be .select2-search-choice";
|
|
|
- }
|
|
|
+ unselect: function (selected) {
|
|
|
+ var val = this.getVal(),
|
|
|
+ index;
|
|
|
|
|
|
- index = indexOf(selected.data("select2-data").id, val);
|
|
|
+ selected = selected.closest(".select2-search-choice");
|
|
|
|
|
|
- if (index >= 0) {
|
|
|
- val.splice(index, 1);
|
|
|
- this.setVal(val);
|
|
|
- if (this.select) this.postprocessResults();
|
|
|
- }
|
|
|
- selected.remove();
|
|
|
- this.triggerChange();
|
|
|
- window.setTimeout(this.bind(this.alignDropdown), 20);
|
|
|
- };
|
|
|
+ if (selected.length === 0) {
|
|
|
+ throw "Invalid argument: " + selected + ". Must be .select2-search-choice";
|
|
|
+ }
|
|
|
|
|
|
- MultiSelect2.prototype.postprocessResults = function () {
|
|
|
- var val = this.getVal(),
|
|
|
- choices = this.results.find(".select2-result"),
|
|
|
- self = this;
|
|
|
+ index = indexOf(selected.data("select2-data").id, val);
|
|
|
|
|
|
- choices.each(function () {
|
|
|
- var choice = $(this), id = choice.data("select2-data").id;
|
|
|
- if (indexOf(id, val) >= 0) {
|
|
|
- choice.addClass("select2-disabled");
|
|
|
- } else {
|
|
|
- choice.removeClass("select2-disabled");
|
|
|
+ if (index >= 0) {
|
|
|
+ val.splice(index, 1);
|
|
|
+ this.setVal(val);
|
|
|
+ if (this.select) this.postprocessResults();
|
|
|
}
|
|
|
- });
|
|
|
+ selected.remove();
|
|
|
+ this.triggerChange();
|
|
|
+ window.setTimeout(this.bind(this.alignDropdown), 20);
|
|
|
+ },
|
|
|
|
|
|
- choices.each(function (i) {
|
|
|
- if (!$(this).hasClass("select2-disabled")) {
|
|
|
- self.highlight(i);
|
|
|
- return false;
|
|
|
- }
|
|
|
- });
|
|
|
+ postprocessResults: function () {
|
|
|
+ var val = this.getVal(),
|
|
|
+ choices = this.results.find(".select2-result"),
|
|
|
+ self = this;
|
|
|
|
|
|
- };
|
|
|
+ choices.each(function () {
|
|
|
+ var choice = $(this), id = choice.data("select2-data").id;
|
|
|
+ if (indexOf(id, val) >= 0) {
|
|
|
+ choice.addClass("select2-disabled");
|
|
|
+ } else {
|
|
|
+ choice.removeClass("select2-disabled");
|
|
|
+ }
|
|
|
+ });
|
|
|
|
|
|
- MultiSelect2.prototype.resizeSearch = function () {
|
|
|
+ choices.each(function (i) {
|
|
|
+ if (!$(this).hasClass("select2-disabled")) {
|
|
|
+ self.highlight(i);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ });
|
|
|
|
|
|
- var minimumWidth, left, maxWidth, containerLeft, searchWidth;
|
|
|
+ },
|
|
|
|
|
|
- minimumWidth = measureTextWidth(this.search) + 10;
|
|
|
+ resizeSearch: function () {
|
|
|
|
|
|
- left = this.search.offset().left;
|
|
|
+ var minimumWidth, left, maxWidth, containerLeft, searchWidth;
|
|
|
|
|
|
- maxWidth = this.selection.width();
|
|
|
- containerLeft = this.selection.offset().left;
|
|
|
+ minimumWidth = measureTextWidth(this.search) + 10;
|
|
|
|
|
|
- searchWidth = maxWidth - (left - containerLeft) - getSideBorderPadding(this.search);
|
|
|
+ left = this.search.offset().left;
|
|
|
|
|
|
- if (searchWidth < minimumWidth) {
|
|
|
- searchWidth = maxWidth - getSideBorderPadding(this.search);
|
|
|
- }
|
|
|
+ maxWidth = this.selection.width();
|
|
|
+ containerLeft = this.selection.offset().left;
|
|
|
|
|
|
- if (searchWidth < 40) {
|
|
|
- searchWidth = maxWidth - getSideBorderPadding(this.search);
|
|
|
- }
|
|
|
- this.search.width(searchWidth);
|
|
|
- };
|
|
|
+ searchWidth = maxWidth - (left - containerLeft) - getSideBorderPadding(this.search);
|
|
|
|
|
|
- MultiSelect2.prototype.getVal = function () {
|
|
|
- var val;
|
|
|
- if (this.select) {
|
|
|
- val = this.select.val();
|
|
|
- return val === null ? [] : val;
|
|
|
- } else {
|
|
|
- val = this.opts.element.val();
|
|
|
- return (val === null || val === "") ? [] : val.split(",");
|
|
|
- }
|
|
|
- };
|
|
|
+ if (searchWidth < minimumWidth) {
|
|
|
+ searchWidth = maxWidth - getSideBorderPadding(this.search);
|
|
|
+ }
|
|
|
|
|
|
- MultiSelect2.prototype.setVal = function (val) {
|
|
|
- var unique = [];
|
|
|
- if (this.select) {
|
|
|
- this.select.val(val);
|
|
|
- } else {
|
|
|
- // filter out duplicates
|
|
|
- $(val).each(function () {
|
|
|
- if (indexOf(this, unique) < 0) unique.push(this);
|
|
|
- });
|
|
|
+ if (searchWidth < 40) {
|
|
|
+ searchWidth = maxWidth - getSideBorderPadding(this.search);
|
|
|
+ }
|
|
|
+ this.search.width(searchWidth);
|
|
|
+ },
|
|
|
|
|
|
- this.opts.element.val(unique.length === 0 ? "" : unique.join(","));
|
|
|
- }
|
|
|
- };
|
|
|
+ getVal: function () {
|
|
|
+ var val;
|
|
|
+ if (this.select) {
|
|
|
+ val = this.select.val();
|
|
|
+ return val === null ? [] : val;
|
|
|
+ } else {
|
|
|
+ val = this.opts.element.val();
|
|
|
+ return (val === null || val === "") ? [] : val.split(",");
|
|
|
+ }
|
|
|
+ },
|
|
|
|
|
|
- MultiSelect2.prototype.val = function () {
|
|
|
- var val, data = [];
|
|
|
+ setVal: function (val) {
|
|
|
+ var unique = [];
|
|
|
+ if (this.select) {
|
|
|
+ this.select.val(val);
|
|
|
+ } else {
|
|
|
+ // filter out duplicates
|
|
|
+ $(val).each(function () {
|
|
|
+ if (indexOf(this, unique) < 0) unique.push(this);
|
|
|
+ });
|
|
|
|
|
|
- if (arguments.length === 0) {
|
|
|
- return this.getVal();
|
|
|
- }
|
|
|
+ this.opts.element.val(unique.length === 0 ? "" : unique.join(","));
|
|
|
+ }
|
|
|
+ },
|
|
|
|
|
|
- val = arguments[0];
|
|
|
+ val: function () {
|
|
|
+ var val, data = [];
|
|
|
|
|
|
- if (this.select) {
|
|
|
- // val is a list of ids
|
|
|
- this.setVal(val);
|
|
|
- this.select.find(":selected").each(function () {
|
|
|
- data.push({id: $(this).attr("value"), text: $(this).text()});
|
|
|
- });
|
|
|
- this.updateSelection(data);
|
|
|
- } else {
|
|
|
- val = (val === null) ? [] : val;
|
|
|
- this.setVal(val);
|
|
|
- // val is a list of objects
|
|
|
+ if (arguments.length === 0) {
|
|
|
+ return this.getVal();
|
|
|
+ }
|
|
|
|
|
|
- $(val).each(function () { data.push(this.id); });
|
|
|
- this.setVal(data);
|
|
|
- this.updateSelection(val);
|
|
|
+ val = arguments[0];
|
|
|
+
|
|
|
+ if (this.select) {
|
|
|
+ // val is a list of ids
|
|
|
+ this.setVal(val);
|
|
|
+ this.select.find(":selected").each(function () {
|
|
|
+ data.push({id: $(this).attr("value"), text: $(this).text()});
|
|
|
+ });
|
|
|
+ this.updateSelection(data);
|
|
|
+ } else {
|
|
|
+ val = (val === null) ? [] : val;
|
|
|
+ this.setVal(val);
|
|
|
+ // val is a list of objects
|
|
|
+
|
|
|
+ $(val).each(function () { data.push(this.id); });
|
|
|
+ this.setVal(data);
|
|
|
+ this.updateSelection(val);
|
|
|
+ }
|
|
|
}
|
|
|
- };
|
|
|
+ });
|
|
|
|
|
|
$.fn.select2 = function () {
|
|
|
|