|
@@ -0,0 +1,781 @@
|
|
|
+/*
|
|
|
+ Licensed to the Apache Software Foundation (ASF) under one
|
|
|
+ or more contributor license agreements. See the NOTICE file
|
|
|
+ distributed with this work for additional information
|
|
|
+ regarding copyright ownership. The ASF licenses this file
|
|
|
+ to you under the Apache License, Version 2.0 (the
|
|
|
+ "License"); you may not use this file except in compliance
|
|
|
+ with the License. You may obtain a copy of the License at
|
|
|
+
|
|
|
+ http://www.apache.org/licenses/LICENSE-2.0
|
|
|
+
|
|
|
+ Unless required by applicable law or agreed to in writing,
|
|
|
+ software distributed under the License is distributed on an
|
|
|
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
|
+ KIND, either express or implied. See the License for the
|
|
|
+ specific language governing permissions and limitations
|
|
|
+ under the License.
|
|
|
+ */
|
|
|
+(function ($) {
|
|
|
+ "use strict";
|
|
|
+ /*global document, window, jQuery, console */
|
|
|
+
|
|
|
+ var KEY, Util, DropDown, ResultList, Selection, Select2, Queries;
|
|
|
+
|
|
|
+ function createClass(def) {
|
|
|
+ var type = function (attrs) {
|
|
|
+ var self = this;
|
|
|
+ if (def.attrs !== undefined) {
|
|
|
+ $.each(def.attrs, function (name, body) {
|
|
|
+ if (attrs[name] !== undefined) {
|
|
|
+ self[name] = attrs[name];
|
|
|
+ } else {
|
|
|
+ if (body.required === true) {
|
|
|
+ throw "Value for required attribute: " + name + " not defined";
|
|
|
+ }
|
|
|
+ if (body.init !== undefined) {
|
|
|
+ self[name] = typeof (body.init) === "function" ? body.init.apply(self) : body.init;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ if (def.methods !== undefined && def.methods.init !== undefined) {
|
|
|
+ self.init(attrs);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ if (def.methods !== undefined) {
|
|
|
+ if (def.methods.bind !== undefined) {
|
|
|
+ throw "Class cannot declare a method called 'bind'";
|
|
|
+ }
|
|
|
+
|
|
|
+ $.each(def.methods, function (name, body) {
|
|
|
+ type.prototype[name] = body;
|
|
|
+ });
|
|
|
+
|
|
|
+ type.prototype.bind = function (func) {
|
|
|
+ var self = this;
|
|
|
+ return function () {
|
|
|
+ func.apply(self, arguments);
|
|
|
+ };
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ return type;
|
|
|
+ }
|
|
|
+
|
|
|
+ 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
|
|
|
+ };
|
|
|
+
|
|
|
+ Util = {};
|
|
|
+
|
|
|
+ Util.debounce = function (threshold, fn) {
|
|
|
+ var timeout;
|
|
|
+ return function () {
|
|
|
+ window.clearTimeout(timeout);
|
|
|
+ timeout = window.setTimeout(fn, threshold);
|
|
|
+ };
|
|
|
+ };
|
|
|
+
|
|
|
+ Util.debounceEvent = function (element, threshold, event, debouncedEvent, direct) {
|
|
|
+ debouncedEvent = debouncedEvent || event + "-debounced";
|
|
|
+ direct = direct || true;
|
|
|
+
|
|
|
+ var notify = Util.debounce(threshold, function (e) {
|
|
|
+ element.trigger(debouncedEvent, e);
|
|
|
+ });
|
|
|
+
|
|
|
+ element.on(event, function (e) {
|
|
|
+ if (direct && element.get().indexOf(e.target) < 0) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ notify(e);
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ (function () {
|
|
|
+
|
|
|
+ var lastpos;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 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
|
|
|
+ */
|
|
|
+ Util.filterMouseEvent = function (element, event, filteredEvent, direct) {
|
|
|
+ filteredEvent = filteredEvent || event + "-filtered";
|
|
|
+ direct = direct || false;
|
|
|
+
|
|
|
+ element.on(event, "*", function (e) {
|
|
|
+ if (direct && element.get().indexOf(e.target) < 0) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (lastpos === undefined || lastpos.x !== e.pageX || lastpos.y !== e.pageY) {
|
|
|
+ $(e.target).trigger(filteredEvent, e);
|
|
|
+ lastpos = {x: e.pageX, y: e.pageY};
|
|
|
+ }
|
|
|
+ });
|
|
|
+ };
|
|
|
+ }());
|
|
|
+
|
|
|
+ DropDown = createClass({
|
|
|
+ attrs: {
|
|
|
+ container: {required: true},
|
|
|
+ element: {required: true},
|
|
|
+ bus: {required: true}
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ open: function () {
|
|
|
+ if (this.isOpen()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ this.container.addClass("select2-dropdown-open");
|
|
|
+
|
|
|
+ // register click-outside-closes-dropdown listener
|
|
|
+ $(document).on("mousedown.dropdown", this.bind(function (e) {
|
|
|
+ var inside = false,
|
|
|
+ container = this.container.get(0);
|
|
|
+ $(e.target).parents().each(function () {
|
|
|
+ return !(inside = (this === container));
|
|
|
+ });
|
|
|
+ if (!inside) {
|
|
|
+ this.close();
|
|
|
+ }
|
|
|
+ }));
|
|
|
+
|
|
|
+ this.element.show();
|
|
|
+ this.bus.trigger("opened");
|
|
|
+ },
|
|
|
+
|
|
|
+ close: function () {
|
|
|
+ if (!this.isOpen()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ this.container.removeClass("select2-dropdown-open");
|
|
|
+
|
|
|
+ $(document).off("mousedown.dropdown");
|
|
|
+ this.element.hide();
|
|
|
+ this.bus.trigger("closed");
|
|
|
+ },
|
|
|
+
|
|
|
+ isOpen: function () {
|
|
|
+ return this.container.hasClass("select2-dropdown-open");
|
|
|
+ },
|
|
|
+
|
|
|
+ toggle: function () {
|
|
|
+ if (this.isOpen()) {
|
|
|
+ this.close();
|
|
|
+ } else {
|
|
|
+ this.open();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ });
|
|
|
+
|
|
|
+ ResultList = createClass({
|
|
|
+ attrs: {
|
|
|
+ element: {required: true},
|
|
|
+ bus: {required: true},
|
|
|
+ formatInputTooShort: {required: true},
|
|
|
+ formatNoMatches: {required: true},
|
|
|
+ formatResult: {required: true},
|
|
|
+ minimumInputLength: {required: true},
|
|
|
+ query: {required: true},
|
|
|
+ selection: {required: true}
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ init: function () {
|
|
|
+ var self = this;
|
|
|
+
|
|
|
+ this.search = this.element.find("input");
|
|
|
+ this.results = this.element.find("ul");
|
|
|
+ this.scrollPosition = 0;
|
|
|
+ this.vars = {};
|
|
|
+
|
|
|
+ this.search.on("keyup", function (e) {
|
|
|
+ if (e.which >= 48 || e.which === KEY.SPACE || e.which === KEY.BACKSPACE || e.which === KEY.DELETE) {
|
|
|
+ self.update();
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ this.search.on("keydown", function (e) {
|
|
|
+ switch (e.which) {
|
|
|
+ case KEY.TAB:
|
|
|
+ e.preventDefault();
|
|
|
+ self.select();
|
|
|
+ return;
|
|
|
+ case KEY.ENTER:
|
|
|
+ e.preventDefault();
|
|
|
+ e.stopPropagation();
|
|
|
+ self.select();
|
|
|
+ return;
|
|
|
+ case KEY.UP:
|
|
|
+ self.moveSelection(-1);
|
|
|
+ e.preventDefault();
|
|
|
+ e.stopPropagation();
|
|
|
+ return;
|
|
|
+ case KEY.DOWN:
|
|
|
+ self.moveSelection(1);
|
|
|
+ e.preventDefault();
|
|
|
+ e.stopPropagation();
|
|
|
+ return;
|
|
|
+ case KEY.ESC:
|
|
|
+ e.preventDefault();
|
|
|
+ e.stopPropagation();
|
|
|
+ self.cancel();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+// this.results.on("mouseleave", "li.select2-result", this.bind(this.unhighlight));
|
|
|
+ Util.filterMouseEvent(this.results, "mousemove");
|
|
|
+ this.results.on("mousemove-filtered", this.bind(function (e) {
|
|
|
+ var el = $(e.target).closest("li.select2-result");
|
|
|
+ if (el.length < 1) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.setSelection(el.index());
|
|
|
+ }));
|
|
|
+ this.results.on("click", this.bind(function (e) {
|
|
|
+ var el = $(e.target).closest("li.select2-result");
|
|
|
+ if (el.length < 1) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.bus.trigger("selected", [el.data("select2-result")]);
|
|
|
+ }));
|
|
|
+
|
|
|
+ Util.debounceEvent(this.results, 100, "scroll");
|
|
|
+
|
|
|
+ this.results.on("scroll-debounced", this.bind(function (e) {
|
|
|
+ this.scrollPosition = this.results.scrollTop();
|
|
|
+
|
|
|
+ var more = this.results.find("li.select2-more-results"), below;
|
|
|
+
|
|
|
+ if (more.length === 0) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // pixels the element is below the scroll fold, below==0 is when the element is starting to be visible
|
|
|
+ below = more.offset().top - this.results.offset().top - this.results.height();
|
|
|
+
|
|
|
+ if (below <= 0) {
|
|
|
+ more.addClass("select2-active");
|
|
|
+ this.query({term: this.search.val(), vars: this.vars, callback: this.bind(this.append)});
|
|
|
+ }
|
|
|
+
|
|
|
+ }));
|
|
|
+ },
|
|
|
+ open: function (e) {
|
|
|
+ this.search.focus();
|
|
|
+ this.results.scrollTop(this.scrollPosition);
|
|
|
+ if (this.results.children().length === 0) {
|
|
|
+ // first time the dropdown is opened, update the results
|
|
|
+ this.update();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ close: function () {
|
|
|
+ //this.search.val("");
|
|
|
+ //this.clear();
|
|
|
+ },
|
|
|
+ clear: function () {
|
|
|
+ this.results.empty();
|
|
|
+ },
|
|
|
+ showInputTooShort: function () {
|
|
|
+ this.show("<li class='select2-no-results'>" + this.formatInputTooShort(this.search.val(), this.minimumInputLength) + "</li>");
|
|
|
+ },
|
|
|
+ showNoMatches: function () {
|
|
|
+ this.show("<li class='select2-no-results'>" + this.formatNoMatches(this.search.val()) + "</li>");
|
|
|
+ },
|
|
|
+ show: function (html) {
|
|
|
+ this.results.html(html);
|
|
|
+ this.results.scrollTop(0);
|
|
|
+ this.search.removeClass("select2-active");
|
|
|
+ },
|
|
|
+ update: function () {
|
|
|
+ var html = "";
|
|
|
+
|
|
|
+ if (this.search.val().length < this.minimumInputLength) {
|
|
|
+ this.showInputTooShort();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ this.search.addClass("select2-active");
|
|
|
+ this.vars = {};
|
|
|
+ this.query({term: this.search.val(), vars: this.vars, callback: this.bind(this.process)});
|
|
|
+ },
|
|
|
+ process: function (data) {
|
|
|
+ if (data.results.length === 0) {
|
|
|
+ this.showNoMatches();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ var html = this.stringizeResults(data.results), selectedId = this.selection.val(), selectedIndex = 0;
|
|
|
+
|
|
|
+ if (data.more === true) {
|
|
|
+ html += "<li class='select2-more-results'>Loading more results...</li>";
|
|
|
+ }
|
|
|
+ this.vars = data.vars || {};
|
|
|
+
|
|
|
+ this.show(html);
|
|
|
+
|
|
|
+ this.findChoices().each(function (i) {
|
|
|
+ if (selectedId === data.results[i].id) {
|
|
|
+ selectedIndex = i;
|
|
|
+ }
|
|
|
+ $(this).data("select2-result", data.results[i]);
|
|
|
+ });
|
|
|
+
|
|
|
+ this.setSelection(selectedIndex);
|
|
|
+
|
|
|
+ },
|
|
|
+ append: function (data) {
|
|
|
+
|
|
|
+ var more = this.results.find("li.select2-more-results"), html, offset;
|
|
|
+
|
|
|
+ this.vars = data.vars || {};
|
|
|
+
|
|
|
+ if (data.results.length === 0) {
|
|
|
+ more.remove();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ html = this.stringizeResults(data.results);
|
|
|
+
|
|
|
+ offset = this.results.find("li.select2-result").length;
|
|
|
+
|
|
|
+ more.before(html);
|
|
|
+
|
|
|
+ this.results.find("li.select2-result").each(function (i) {
|
|
|
+ if (i >= offset) {
|
|
|
+ $(this).data("select2-result", data.results[i - offset]);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ if (data.more !== true) {
|
|
|
+ more.remove();
|
|
|
+ } else {
|
|
|
+ more.removeClass("select2-active");
|
|
|
+ }
|
|
|
+
|
|
|
+ },
|
|
|
+ stringizeResults: function (results, html) {
|
|
|
+ var i, l, classes;
|
|
|
+ html = html || "";
|
|
|
+ for (i = 0, l = results.length; i < l; i += 1) {
|
|
|
+ html += "<li class='select2-result'>";
|
|
|
+ html += this.formatResult(results[i]);
|
|
|
+ html += "</li>";
|
|
|
+ }
|
|
|
+ return html;
|
|
|
+ },
|
|
|
+
|
|
|
+ findChoices: function () {
|
|
|
+ return this.results.children("li.select2-result");
|
|
|
+ },
|
|
|
+
|
|
|
+ removeSelection: function () {
|
|
|
+ this.findChoices().each(function () {
|
|
|
+ $(this).removeClass("select2-highlighted");
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ setSelection: function (index) {
|
|
|
+ this.removeSelection();
|
|
|
+
|
|
|
+ var children = this.findChoices(),
|
|
|
+ child = $(children[index]),
|
|
|
+ hb,
|
|
|
+ rb,
|
|
|
+ y,
|
|
|
+ more;
|
|
|
+
|
|
|
+ child.addClass("select2-highlighted");
|
|
|
+
|
|
|
+ this.search.focus();
|
|
|
+
|
|
|
+ hb = child.offset().top + child.outerHeight();
|
|
|
+
|
|
|
+ // if this is the last child lets also make sure select2-more-results is visible
|
|
|
+ if (index === children.length - 1) {
|
|
|
+ more = this.results.find("li.select2-more-results");
|
|
|
+ if (more.length > 0) {
|
|
|
+ hb = more.offset().top + more.outerHeight();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ rb = this.results.offset().top + this.results.outerHeight();
|
|
|
+ if (hb > rb) {
|
|
|
+ this.results.scrollTop(this.results.scrollTop() + (hb - rb));
|
|
|
+ }
|
|
|
+ y = child.offset().top - this.results.offset().top;
|
|
|
+
|
|
|
+ // make sure the top of the element is visible
|
|
|
+ if (y < 0) {
|
|
|
+ this.results.scrollTop(this.results.scrollTop() + y); // y is negative
|
|
|
+ }
|
|
|
+
|
|
|
+ },
|
|
|
+
|
|
|
+ getSelectionIndex: function () {
|
|
|
+ var children = this.findChoices(), i = 0, l = children.length;
|
|
|
+ for (; i < l; i += 1) {
|
|
|
+ if ($(children[i]).hasClass("select2-highlighted")) {
|
|
|
+ return i;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return -1;
|
|
|
+ },
|
|
|
+
|
|
|
+ moveSelection: function (delta) {
|
|
|
+ var current = this.getSelectionIndex(),
|
|
|
+ children = this.findChoices(),
|
|
|
+ next = current + delta;
|
|
|
+
|
|
|
+ if (current >= 0 && next >= 0 && next < children.length) {
|
|
|
+ this.setSelection(next);
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ select: function () {
|
|
|
+ var selected = this.results.find("li.select2-highlighted");
|
|
|
+ if (selected.length > 0) {
|
|
|
+ this.bus.trigger("selected", [selected.data("select2-result")]);
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ cancel: function () {
|
|
|
+ this.bus.trigger("cancelled");
|
|
|
+ },
|
|
|
+
|
|
|
+ val: function (data) {
|
|
|
+ var choices = this.findChoices(), index;
|
|
|
+
|
|
|
+ choices.each(function (i) {
|
|
|
+ if ($(this).data("select2-result").id === data) {
|
|
|
+ index = i;
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ if (index === undefined && data.id !== undefined) {
|
|
|
+ choices.each(function (i) {
|
|
|
+ if ($(this).data("select2-result").id === data.id) {
|
|
|
+ index = i;
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ if (index !== undefined) {
|
|
|
+ this.setSelection(index);
|
|
|
+ this.select();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ this.bus.trigger("selected", data);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ Selection = createClass({
|
|
|
+ attrs: {
|
|
|
+ bus: {required: true},
|
|
|
+ element: {required: true},
|
|
|
+ display: {init: function () {
|
|
|
+ return this.element.find("span");
|
|
|
+ }},
|
|
|
+ hidden: {required: true},
|
|
|
+ formatSelection: {required: true},
|
|
|
+ placeholder: {},
|
|
|
+ dropdown: {required: true}
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ init: function () {
|
|
|
+ if (this.placeholder) {
|
|
|
+ this.select(this.placeholder);
|
|
|
+ }
|
|
|
+ this.element.click(this.dropdown.bind(this.dropdown.toggle));
|
|
|
+
|
|
|
+ var self = this;
|
|
|
+ this.element.on("keydown", function (e) {
|
|
|
+ switch (e.which) {
|
|
|
+ case KEY.TAB:
|
|
|
+ case KEY.SHIFT:
|
|
|
+ case KEY.CTRL:
|
|
|
+ case KEY.ALT:
|
|
|
+ case KEY.LEFT:
|
|
|
+ case KEY.RIGHT:
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ self.dropdown.open();
|
|
|
+ });
|
|
|
+ },
|
|
|
+ select: function (data) {
|
|
|
+ this.display.html(this.formatSelection(data));
|
|
|
+ this.hidden.val(data.id);
|
|
|
+ },
|
|
|
+ focus: function () {
|
|
|
+ this.element.focus();
|
|
|
+ },
|
|
|
+ val: function () {
|
|
|
+ return this.hidden.val();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ });
|
|
|
+
|
|
|
+ Queries = {};
|
|
|
+ Queries.select = function (select2, element) {
|
|
|
+ var options = [];
|
|
|
+ element.find("option").each(function () {
|
|
|
+ var e = $(this);
|
|
|
+ options.push({id: e.attr("value"), text: e.text()});
|
|
|
+ });
|
|
|
+ return function (query) {
|
|
|
+ var data = {results: [], more: false},
|
|
|
+ text = query.term.toUpperCase();
|
|
|
+ $.each(options, function (i) {
|
|
|
+ if (this.text.toUpperCase().indexOf(text) >= 0) {
|
|
|
+ data.results.push(this);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ query.callback(data);
|
|
|
+ };
|
|
|
+ };
|
|
|
+ Queries.ajax = function (select2, el) {
|
|
|
+ var timeout, // current scheduled but not yet executed request
|
|
|
+ requestSequence = 0, // sequence used to drop out-of-order responses
|
|
|
+ quietMillis = select2.ajax.quietMillis || 100;
|
|
|
+
|
|
|
+ return function (query) {
|
|
|
+ window.clearTimeout(timeout);
|
|
|
+ timeout = window.setTimeout(function () {
|
|
|
+ requestSequence += 1; // increment the sequence
|
|
|
+ var requestNumber = requestSequence, // this request's sequence number
|
|
|
+ options = select2.ajax, // ajax parameters
|
|
|
+ data = options.data; // ajax data function
|
|
|
+
|
|
|
+ data = data.call(this, query.term, query.vars);
|
|
|
+
|
|
|
+ $.ajax({
|
|
|
+ url: options.url,
|
|
|
+ dataType: options.dataType,
|
|
|
+ data: data
|
|
|
+ }).success(
|
|
|
+ function (data) {
|
|
|
+ if (requestNumber < requestSequence) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ query.callback(options.results(data, query.vars));
|
|
|
+ }
|
|
|
+ );
|
|
|
+ }, quietMillis);
|
|
|
+ };
|
|
|
+ };
|
|
|
+
|
|
|
+ Select2 = createClass({
|
|
|
+ attrs: {
|
|
|
+ el: {required: true},
|
|
|
+ formatResult: {init: function () {
|
|
|
+ return function (data) {
|
|
|
+ return data.text;
|
|
|
+ };
|
|
|
+ }},
|
|
|
+ formatSelection: {init: function () {
|
|
|
+ return function (data) {
|
|
|
+ return data.text;
|
|
|
+ };
|
|
|
+ }},
|
|
|
+ formatNoMatches: {init: function () {
|
|
|
+ return function () {
|
|
|
+ return "No matches found";
|
|
|
+ };
|
|
|
+ }},
|
|
|
+ formatInputTooShort: {init: function () {
|
|
|
+ return function (input, min) {
|
|
|
+ return "Please enter " + (min - input.length) + " more characters to start search";
|
|
|
+ };
|
|
|
+ }},
|
|
|
+ minimumInputLength: {init: 0},
|
|
|
+ placeholder: {init: undefined},
|
|
|
+ ajax: {init: undefined},
|
|
|
+ query: {init: undefined}
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ init: function () {
|
|
|
+ var self = this, width, dropdown, results, selected, select;
|
|
|
+
|
|
|
+ this.el = $(this.el);
|
|
|
+
|
|
|
+ width = this.el.outerWidth();
|
|
|
+
|
|
|
+ this.container = $("<div></div>", {
|
|
|
+ "class": "select2-container",
|
|
|
+ style: "width: " + width + "px"
|
|
|
+ });
|
|
|
+ this.container.html(
|
|
|
+ " <a href='javascript:void(0)' class='select2-choice'>" +
|
|
|
+ " <span></span>" +
|
|
|
+ " <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>" +
|
|
|
+ "<input type='hidden'/>"
|
|
|
+ );
|
|
|
+
|
|
|
+ this.el.data("select2", this);
|
|
|
+ this.el.hide();
|
|
|
+ this.el.after(self.container);
|
|
|
+ if (this.el.attr("class") !== undefined) {
|
|
|
+ this.container.addClass(this.el.attr("class"));
|
|
|
+ }
|
|
|
+ this.container.data("select2", this);
|
|
|
+ this.container.find("input[type=hidden]").attr("name", this.el.attr("name"));
|
|
|
+
|
|
|
+ if (this.query === undefined && this.el.get(0).tagName.toUpperCase() === "SELECT") {
|
|
|
+ this.query = "select";
|
|
|
+ select = true;
|
|
|
+ }
|
|
|
+ if (Queries[this.query] !== undefined) {
|
|
|
+ this.query = Queries[this.query](this, this.el);
|
|
|
+ }
|
|
|
+
|
|
|
+ (function () {
|
|
|
+
|
|
|
+ var dropdown, searchContainer, search, width;
|
|
|
+
|
|
|
+ function getSideBorderPadding(e) {
|
|
|
+ return e.outerWidth() - e.width();
|
|
|
+ }
|
|
|
+
|
|
|
+ // position and size dropdown
|
|
|
+ dropdown = self.container.find("div.select2-drop");
|
|
|
+ width = self.container.outerWidth() - getSideBorderPadding(dropdown);
|
|
|
+ dropdown.css({top: self.container.height(), width: width});
|
|
|
+
|
|
|
+ // size search field
|
|
|
+ searchContainer = self.container.find(".select2-search");
|
|
|
+ search = searchContainer.find("input");
|
|
|
+ width = dropdown.width();
|
|
|
+ width -= getSideBorderPadding(searchContainer);
|
|
|
+ width -= getSideBorderPadding(search);
|
|
|
+ search.css({width: width});
|
|
|
+ }());
|
|
|
+
|
|
|
+ dropdown = new DropDown({
|
|
|
+ element: this.container.find("div.select2-drop"),
|
|
|
+ container: this.container,
|
|
|
+ bus: this.el
|
|
|
+ });
|
|
|
+
|
|
|
+ this.selection = new Selection({
|
|
|
+ bus: this.el,
|
|
|
+ element: this.container.find(".select2-choice"),
|
|
|
+ hidden: this.container.find("input[type=hidden]"),
|
|
|
+ formatSelection: this.formatSelection,
|
|
|
+ placeholder: this.placeholder,
|
|
|
+ dropdown: dropdown
|
|
|
+ });
|
|
|
+
|
|
|
+ this.results = new ResultList({
|
|
|
+ element: this.container.find("div.select2-drop"),
|
|
|
+ bus: this.el,
|
|
|
+ formatInputTooShort: this.formatInputTooShort,
|
|
|
+ formatNoMatches: this.formatNoMatches,
|
|
|
+ formatResult: this.formatResult,
|
|
|
+ minimumInputLength: this.minimumInputLength,
|
|
|
+ query: this.query,
|
|
|
+ selection: this.selection
|
|
|
+ });
|
|
|
+
|
|
|
+ this.el.on("selected", function (e, result) {
|
|
|
+ dropdown.close();
|
|
|
+ self.selection.select(result);
|
|
|
+ });
|
|
|
+
|
|
|
+ this.el.on("cancelled", function () {
|
|
|
+ dropdown.close();
|
|
|
+ });
|
|
|
+
|
|
|
+ this.el.on("opened", this.bind(function () {
|
|
|
+ this.results.open();
|
|
|
+ }));
|
|
|
+
|
|
|
+ this.el.on("closed", this.bind(function () {
|
|
|
+ this.container.removeClass("select2-dropdown-open");
|
|
|
+ this.results.close();
|
|
|
+ this.selection.focus();
|
|
|
+ }));
|
|
|
+
|
|
|
+ // if attached to a select do some default initialization
|
|
|
+ if (select) {
|
|
|
+ this.results.update(); // build the results
|
|
|
+ selected = this.el.find("option[selected]");
|
|
|
+ if (selected.length < 1 && this.placeholder === undefined) {
|
|
|
+ selected = $(this.el.find("option")[0]);
|
|
|
+ }
|
|
|
+ if (selected.length > 0) {
|
|
|
+ this.val({id: selected.attr("value"), text: selected.text()});
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ },
|
|
|
+ val: function () {
|
|
|
+ var data;
|
|
|
+ if (arguments.length === 0) {
|
|
|
+ return this.selection.val();
|
|
|
+ } else {
|
|
|
+ data = arguments[0];
|
|
|
+ this.results.val(data);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ $.fn.select2 = function () {
|
|
|
+ var args = Array.prototype.slice.call(arguments, 0), value, tmp;
|
|
|
+ this.each(function () {
|
|
|
+ if (args.length === 0) {
|
|
|
+ tmp = new Select2({el: this});
|
|
|
+ } else if (typeof (args[0]) === "object") {
|
|
|
+ args[0].el = this;
|
|
|
+ tmp = new Select2(args[0]);
|
|
|
+ } else if (typeof (args[0]) === "string") {
|
|
|
+ var select2 = $(this).data("select2");
|
|
|
+ value = select2[args[0]].apply(select2, args.slice(1));
|
|
|
+ return false;
|
|
|
+ } else {
|
|
|
+ throw "Invalid arguments to select2 plugin: " + args;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ return (value === undefined) ? this : value;
|
|
|
+ };
|
|
|
+
|
|
|
+}(jQuery));
|