PageSourceSearch

https://www.malbert.fr/js/select2.js

js malbert.fr collected 2026-10-04 02:58:56 UTC 150,973 bytes, 3,522 lines download raw bytes

1/*
2Copyright 2012 Igor Vaynberg
3
4Version: 3.5.1 Timestamp: Tue Jul 22 18:58:56 EDT 2014
5
6This software is licensed under the Apache License, Version 2.0 (the "Apache License") or the GNU
7General Public License version 2 (the "GPL License"). You may choose either license to govern your
8use of this software only upon the condition that you accept all of the terms of either the Apache
9License or the GPL License.
10
11You may obtain a copy of the Apache License and the GPL License at:
12
13    http://www.apache.org/licenses/LICENSE-2.0
14    http://www.gnu.org/licenses/gpl-2.0.html
15
16Unless required by applicable law or agreed to in writing, software distributed under the
17Apache License or the GPL License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
18CONDITIONS OF ANY KIND, either express or implied. See the Apache License and the GPL License for
19the specific language governing permissions and limitations under the Apache License and the GPL License.
20*/
21(function ($) {
22    if(typeof $.fn.each2 == "undefined") {
23        $.extend($.fn, {
24            /*
25            * 4-10 times faster .each replacement
26            * use it carefully, as it overrides jQuery context of element on each iteration
27            */
28            each2 : function (c) {
29                var j = $([0]), i = -1, l = this.length;
30                while (
31                    ++i < l
32                    && (j.context = j[0] = this[i])
33                    && c.call(j[0], i, j) !== false //"this"=DOM, i=index, j=jQuery object
34                );
35                return this;
36            }
37        });
38    }
39})(jQuery);
40
41(function ($, undefined) {
42    "use strict";
43    /*global document, window, jQuery, console */
44
45    if (window.Select2 !== undefined) {
46        return;
47    }
48
49    var KEY, AbstractSelect2, SingleSelect2, MultiSelect2, nextUid, sizer,
50        lastMousePosition={x:0,y:0}, $document, scrollBarDimensions,
51
52    KEY = {
53        TAB: 9,
54        ENTER: 13,
55        ESC: 27,
56        SPACE: 32,
57        LEFT: 37,
58        UP: 38,
59        RIGHT: 39,
60        DOWN: 40,
61        SHIFT: 16,
62        CTRL: 17,
63        ALT: 18,
64        PAGE_UP: 33,
65        PAGE_DOWN: 34,
66        HOME: 36,
67        END: 35,
68        BACKSPACE: 8,
69        DELETE: 46,
70        isArrow: function (k) {
71            k = k.which ? k.which : k;
72            switch (k) {
73            case KEY.LEFT:
74            case KEY.RIGHT:
75            case KEY.UP:
76            case KEY.DOWN:
77                return true;
78            }
79            return false;
80        },
81        isControl: function (e) {
82            var k = e.which;
83            switch (k) {
84            case KEY.SHIFT:
85            case KEY.CTRL:
86            case KEY.ALT:
87                return true;
88            }
89
90            if (e.metaKey) return true;
91
92            return false;
93        },
94        isFunctionKey: function (k) {
95            k = k.which ? k.which : k;
96            return k >= 112 && k <= 123;
97        }
98    },
99    MEASURE_SCROLLBAR_TEMPLATE = "<div class='select2-measure-scrollbar'></div>",
100
101    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"
101:"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","\u1EC
1015":"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"};
102
103    $document = $(document);
104
105    nextUid=(function() { var counter=1; return function() { return counter++; }; }());
106
107
108    function reinsertElement(element) {
109        var placeholder = $(document.createTextNode(''));
110
111        element.before(placeholder);
112        placeholder.before(element);
113        placeholder.remove();
114    }
115
116    function stripDiacritics(str) {
117        // Used 'uni range + named function' from http://jsperf.com/diacritics/18
118        function match(a) {
119            return DIACRITICS[a] || a;
120        }
121
122        return str.replace(/[^\u0000-\u007E]/g, match);
123    }
124
125    function indexOf(value, array) {
126        var i = 0, l = array.length;
127        for (; i < l; i = i + 1) {
128            if (equal(value, array[i])) return i;
129        }
130        return -1;
131    }
132
133    function measureScrollbar () {
134        var $template = $( MEASURE_SCROLLBAR_TEMPLATE );
135        $template.appendTo('body');
136
137        var dim = {
138            width: $template.width() - $template[0].clientWidth,
139            height: $template.height() - $template[0].clientHeight
140        };
141        $template.remove();
142
143        return dim;
144    }
145
146    /**
147     * Compares equality of a and b
148     * @param a
149     * @param b
150     */
151    function equal(a, b) {
152        if (a === b) return true;
153        if (a === undefined || b === undefined) return false;
154        if (a === null || b === null) return false;
155        // Check whether 'a' or 'b' is a string (primitive or object).
156        // The concatenation of an empty string (+'') converts its argument to a string's primitive.
157        if (a.constructor === String) return a+'' === b+''; // a+'' - in case 'a' is a String object
158        if (b.constructor === String) return b+'' === a+''; // b+'' - in case 'b' is a String object
159        return false;
160    }
161
162    /**
163     * Splits the string into an array of values, trimming each value. An empty array is returned for nulls or empty
164     * strings
165     * @param string
166     * @param separator
167     */
168    function splitVal(string, separator) {
169        var val, i, l;
170        if (string === null || string.length < 1) return [];
171        val = string.split(separator);
172        for (i = 0, l = val.length; i < l; i = i + 1) val[i] = $.trim(val[i]);
173        return val;
174    }
175
176    function getSideBorderPadding(element) {
177        return element.outerWidth(false) - element.width();
178    }
179
180    function installKeyUpChangeEvent(element) {
181        var key="keyup-change-value";
182        element.on("keydown", function () {
183            if ($.data(element, key) === undefined) {
184                $.data(element, key, element.val());
185            }
186        });
187        element.on("keyup", function () {
188            var val= $.data(element, key);
189            if (val !== undefined && element.val() !== val) {
190                $.removeData(element, key);
191                element.trigger("keyup-change");
192            }
193        });
194    }
195
196
197    /**
198     * filters mouse events so an event is fired only if the mouse moved.
199     *
200     * filters out mouse events that occur when mouse is stationary but
201     * the elements under the pointer are scrolled.
202     */
203    function installFilteredMouseMove(element) {
204        element.on("mousemove", function (e) {
205            var lastpos = lastMousePosition;
206            if (lastpos === undefined || lastpos.x !== e.pageX || lastpos.y !== e.pageY) {
207                $(e.target).trigger("mousemove-filtered", e);
208            }
209        });
210    }
211
212    /**
213     * Debounces a function. Returns a function that calls the original fn function only if no invocations have been made
214     * within the last quietMillis milliseconds.
215     *
216     * @param quietMillis number of milliseconds to wait before invoking fn
217     * @param fn function to be debounced
218     * @param ctx object to be used as this reference within fn
219     * @return debounced version of fn
220     */
221    function debounce(quietMillis, fn, ctx) {
222        ctx = ctx || undefined;
223        var timeout;
224        return function () {
225            var args = arguments;
226            window.clearTimeout(timeout);
227            timeout = window.setTimeout(function() {
228                fn.apply(ctx, args);
229            }, quietMillis);
230        };
231    }
232
233    function installDebouncedScroll(threshold, element) {
234        var notify = debounce(threshold, function (e) { element.trigger("scroll-debounced", e);});
235        element.on("scroll", function (e) {
236            if (indexOf(e.target, element.get()) >= 0) notify(e);
237        });
238    }
239
240    function focus($el) {
241        if ($el[0] === document.activeElement) return;
242
243        /* set the focus in a 0 timeout - that way the focus is set after the processing
244            of the current event has finished - which seems like the only reliable way
245            to set focus */
246        window.setTimeout(function() {
247            var el=$el[0], pos=$el.val().length, range;
248
249            $el.focus();
250
251            /* make sure el received focus so we do not error out when trying to manipulate the caret.
252                sometimes modals or others listeners may steal it after its set */
253            var isVisible = (el.offsetWidth > 0 || el.offsetHeight > 0);
254            if (isVisible && el === document.activeElement) {
255
256                /* after the focus is set move the caret to the end, necessary when we val()
257                    just before setting focus */
258                if(el.setSelectionRange)
259                {
260                    el.setSelectionRange(pos, pos);
261                }
262                else if (el.createTextRange) {
263                    range = el.createTextRange();
264                    range.collapse(false);
265                    range.select();
266                }
267            }
268        }, 0);
269    }
270
271    function getCursorInfo(el) {
272        el = $(el)[0];
273        var offset = 0;
274        var length = 0;
275        if ('selectionStart' in el) {
276            offset = el.selectionStart;
277            length = el.selectionEnd - offset;
278        } else if ('selection' in document) {
279            el.focus();
280            var sel = document.selection.createRange();
281            length = document.selection.createRange().text.length;
282            sel.moveStart('character', -el.value.length);
283            offset = sel.text.length - length;
284        }
285        return { offset: offset, length: length };
286    }
287
288    function killEvent(event) {
289        event.preventDefault();
290        event.stopPropagation();
291    }
292    function killEventImmediately(event) {
293        event.preventDefault();
294        event.stopImmediatePropagation();
295    }
296
297    function measureTextWidth(e) {
298        if (!sizer){
299            var style = e[0].currentStyle || window.getComputedStyle(e[0], null);
300            sizer = $(document.createElement("div")).css({
301                position: "absolute",
302                left: "-10000px",
303                top: "-10000px",
304                display: "none",
305                fontSize: style.fontSize,
306                fontFamily: style.fontFamily,
307                fontStyle: style.fontStyle,
308                fontWeight: style.fontWeight,
309                letterSpacing: style.letterSpacing,
310                textTransform: style.textTransform,
311                whiteSpace: "nowrap"
312            });
313            sizer.attr("class","select2-sizer");
314            $("body").append(sizer);
315        }
316        sizer.text(e.val());
317        return sizer.width();
318    }
319
320    function syncCssClasses(dest, src, adapter) {
321        var classes, replacements = [], adapted;
322
323        classes = $.trim(dest.attr("class"));
324
325        if (classes) {
326            classes = '' + classes; // for IE which returns object
327
328            $(classes.split(/\s+/)).each2(function() {
329                if (this.indexOf("select2-") === 0) {
330                    replacements.push(this);
331                }
332            });
333        }
334
335        classes = $.trim(src.attr("class"));
336
337        if (classes) {
338            classes = '' + classes; // for IE which returns object
339
340            $(classes.split(/\s+/)).each2(function() {
341                if (this.indexOf("select2-") !== 0) {
342                    adapted = adapter(this);
343
344                    if (adapted) {
345                        replacements.push(adapted);
346                    }
347                }
348            });
349        }
350
351        dest.attr("class", replacements.join(" "));
352    }
353
354
355    function markMatch(text, term, markup, escapeMarkup) {
356        var match=stripDiacritics(text.toUpperCase()).indexOf(stripDiacritics(term.toUpperCase())),
357            tl=term.length;
358
359        if (match<0) {
360            markup.push(escapeMarkup(text));
361            return;
362        }
363
364        markup.push(escapeMarkup(text.substring(0, match)));
365        markup.push("<span class='select2-match'>");
366        markup.push(escapeMarkup(text.substring(match, match + tl)));
367        markup.push("</span>");
368        markup.push(escapeMarkup(text.substring(match + tl, text.length)));
369    }
370
371    function defaultEscapeMarkup(markup) {
372        var replace_map = {
373            '\\': '&#92;',
374            '&': '&amp;',
375            '<': '&lt;',
376            '>': '&gt;',
377            '"': '&quot;',
378            "'": '&#39;',
379            "/": '&#47;'
380        };
381
382        return String(markup).replace(/[&<>"'\/\\]/g, function (match) {
383            return replace_map[match];
384        });
385    }
386
387    /**
388     * Produces an ajax-based query function
389     *
390     * @param options object containing configuration parameters
391     * @param options.params parameter map for the transport ajax call, can contain such options as cache, jsonpCallback, etc. see $.ajax
392     * @param options.transport function that will be used to execute the ajax request. must be compatible with parameters supported by $.ajax
393     * @param options.url url for the data
394     * @param options.data a function(searchTerm, pageNumber, context) that should return an object c
394ontaining query string parameters for the above url.
395     * @param options.dataType request data type: ajax, jsonp, other datatypes supported by jQuery's $.ajax function or the transport function if specified
396     * @param options.quietMillis (optional) milliseconds to wait before making the ajaxRequest, helps debounce the ajax function if invoked too often
397     * @param options.results a function(remoteData, pageNumber, query) that converts data returned form the remote request to the format expected by Select2.
398     *      The expected format is an object containing the following keys:
399     *      results array of objects that will be used as choices
400     *      more (optional) boolean indicating whether there are more results available
401     *      Example: {results:[{id:1, text:'Red'},{id:2, text:'Blue'}], more:true}
402     */
403    function ajax(options) {
404        var timeout, // current scheduled but not yet executed request
405            handler = null,
406            quietMillis = options.quietMillis || 100,
407            ajaxUrl = options.url,
408            self = this;
409
410        return function (query) {
411            window.clearTimeout(timeout);
412            timeout = window.setTimeout(function () {
413                var data = options.data, // ajax data function
414                    url = ajaxUrl, // ajax url string or function
415                    transport = options.transport || $.fn.select2.ajaxDefaults.transport,
416                    // deprecated - to be removed in 4.0  - use params instead
417                    deprecated = {
418                        type: options.type || 'GET', // set type of request (GET or POST)
419                        cache: options.cache || false,
420                        jsonpCallback: options.jsonpCallback||undefined,
421                        dataType: options.dataType||"json"
422                    },
423                    params = $.extend({}, $.fn.select2.ajaxDefaults.params, deprecated);
424
425                data = data ? data.call(self, query.term, query.page, query.context) : null;
426                url = (typeof url === 'function') ? url.call(self, query.term, query.page, query.context) : url;
427
428                if (handler && typeof handler.abort === "function") { handler.abort(); }
429
430                if (options.params) {
431                    if ($.isFunction(options.params)) {
432                        $.extend(params, options.params.call(self));
433                    } else {
434                        $.extend(params, options.params);
435                    }
436                }
437
438                $.extend(params, {
439                    url: url,
440                    dataType: options.dataType,
441                    data: data,
442                    success: function (data) {
443                        // TODO - replace query.page with query so users have access to term, page, etc.
444                        // added query as third paramter to keep backwards compatibility
445                        var results = options.results(data, query.page, query);
446                        query.callback(results);
447                    },
448                    error: function(jqXHR, textStatus, errorThrown){
449                        var results = {
450                            hasError: true,
451                            jqXHR: jqXHR,
452                            textStatus: textStatus,
453                            errorThrown: errorThrown,
454                        };
455
456                        query.callback(results);
457                    }
458                });
459                handler = transport.call(self, params);
460            }, quietMillis);
461        };
462    }
463
464    /**
465     * Produces a query function that works with a local array
466     *
467     * @param options object containing configuration parameters. The options parameter can either be an array or an
468     * object.
469     *
470     * If the array form is used it is assumed that it contains objects with 'id' and 'text' keys.
471     *
472     * If the object form is used it is assumed that it contains 'data' and 'text' keys. The 'data' key should contain
473     * an array of objects that will be used as choices. These objects must contain at least an 'id' key. The 'text'
474     * key can either be a String in which case it is expected that each element in the 'data' array has a key with the
475     * value of 'text' which will be used to match choices. Alternatively, text can be a function(item) that can extract
476     * the text.
477     */
478    function local(options) {
479        var data = options, // data elements
480            dataText,
481            tmp,
482            text = function (item) { return ""+item.text; }; // function used to retrieve the text portion of a data item that is matched against the search
483
484         if ($.isArray(data)) {
485            tmp = data;
486            data = { results: tmp };
487        }
488
489         if ($.isFunction(data) === false) {
490            tmp = data;
491            data = function() { return tmp; };
492        }
493
494        var dataItem = data();
495        if (dataItem.text) {
496            text = dataItem.text;
497            // if text is not a function we assume it to be a key name
498            if (!$.isFunction(text)) {
499                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
500                text = function (item) { return item[dataText]; };
501            }
502        }
503
504        return function (query) {
505            var t = query.term, filtered = { results: [] }, process;
506            if (t === "") {
507                query.callback(data());
508                return;
509            }
510
511            process = function(datum, collection) {
512                var group, attr;
513                datum = datum[0];
514                if (datum.children) {
515                    group = {};
516                    for (attr in datum) {
517                        if (datum.hasOwnProperty(attr)) group[attr]=datum[attr];
518                    }
519                    group.children=[];
520                    $(datum.children).each2(function(i, childDatum) { process(childDatum, group.children); });
521                    if (group.children.length || query.matcher(t, text(group), datum)) {
522                        collection.push(group);
523                    }
524                } else {
525                    if (query.matcher(t, text(datum), datum)) {
526                        collection.push(datum);
527                    }
528                }
529            };
530
531            $(data().results).each2(function(i, datum) { process(datum, filtered.results); });
532            query.callback(filtered);
533        };
534    }
535
536    // TODO javadoc
537    function tags(data) {
538        var isFunc = $.isFunction(data);
539        return function (query) {
540            var t = query.term, filtered = {results: []};
541            var result = isFunc ? data(query) : data;
542            if ($.isArray(result)) {
543                $(result).each(function () {
544                    var isObject = this.text !== undefined,
545                        text = isObject ? this.text : this;
546                    if (t === "" || query.matcher(t, text)) {
547                        filtered.results.push(isObject ? this : {id: this, text: this});
548                    }
549                });
550                query.callback(filtered);
551            }
552        };
553    }
554
555    /**
556     * Checks if the formatter function should be used.
557     *
558     * Throws an error if it is not a function. Returns true if it should be used,
559     * false if no formatting should be performed.
560     *
561     * @param formatter
562     */
563    function checkFormatter(formatter, formatterName) {
564        if ($.isFunction(formatter)) return true;
565        if (!formatter) return false;
566        if (typeof(formatter) === 'string') return true;
567        throw new Error(formatterName +" must be a string, function, or falsy value");
568    }
569
570  /**
571   * Returns a given value
572   * If given a function, returns its output
573   *
574   * @param val string|function
575   * @param context value of "this" to be passed to function
576   * @returns {*}
577   */
578    function evaluate(val, context) {
579        if ($.isFunction(val)) {
580            var args = Array.prototype.slice.call(arguments, 2);
581            return val.apply(context, args);
582        }
583        return val;
584    }
585
586    function countResults(results) {
587        var count = 0;
588        $.each(results, function(i, item) {
589            if (item.children) {
590                count += countResults(item.children);
591            } else {
592                count++;
593            }
594        });
595        return count;
596    }
597
598    /**
599     * Default tokenizer. This function uses breaks the input on substring match of any string from the
600     * opts.tokenSeparators array and uses opts.createSearchChoice to create the choice object. Both of those
601     * two options have to be defined in order for the tokenizer to work.
602     *
603     * @param input text user has typed so far or pasted into the search field
604     * @param selection currently selected choices
605     * @param selectCallback function(choice) callback tho add the choice to selection
606     * @param opts select2's opts
607     * @return undefined/null to leave the current input unchanged, or a string to change the input to the returned value
608     */
609    function defaultTokenizer(input, selection, selectCallback, opts) {
610        var original = input, // store the original so we can compare and know if we need to tell the search to update its text
611            dupe = false, // check for whether a token we extracted represents a duplicate selected choice
612            token, // token
613            index, // position at which the separator was found
614            i, l, // looping variables
615            separator; // the matched separator
616
617        if (!opts.createSearchChoice || !opts.tokenSeparators || opts.tokenSeparators.length < 1) return undefined;
618
619        while (true) {
620            index = -1;
621
622            for (i = 0, l = opts.tokenSeparators.length; i < l; i++) {
623                separator = opts.tokenSeparators[i];
624                index = input.indexOf(separator);
625                if (index >= 0) break;
626            }
627
628            if (index < 0) break; // did not find any token separator in the input string, bail
629
630            token = input.substring(0, index);
631            input = input.substring(index + separator.length);
632
633            if (token.length > 0) {
634                token = opts.createSearchChoice.call(this, token, selection);
635                if (token !== undefined && token !== null && opts.id(token) !== undefined && opts.id(token) !== null) {
636                    dupe = false;
637                    for (i = 0, l = selection.length; i < l; i++) {
638                        if (equal(opts.id(token), opts.id(selection[i]))) {
639                            dupe = true; break;
640                        }
641                    }
642
643                    if (!dupe) selectCallback(token);
644                }
645            }
646        }
647
648        if (original!==input) return input;
649    }
650
651    function cleanupJQueryElements() {
652        var self = this;
653
654        $.each(arguments, function (i, element) {
655            self[element].remove();
656            self[element] = null;
657        });
658    }
659
660    /**
661     * Creates a new class
662     *
663     * @param superClass
664     * @param methods
665     */
666    function clazz(SuperClass, methods) {
667        var constructor = function () {};
668        constructor.prototype = new SuperClass;
669        constructor.prototype.constructor = constructor;
670        constructor.prototype.parent = SuperClass.prototype;
671        constructor.prototype = $.extend(constructor.prototype, methods);
672        return constructor;
673    }
674
675    AbstractSelect2 = clazz(Object, {
676
677        // abstract
678        bind: function (func) {
679            var self = this;
680            return function () {
681                func.apply(self, arguments);
682            };
683        },
684
685        // abstract
686        init: function (opts) {
687            var results, search, resultsSelector = ".select2-results";
688
689            // prepare options
690            this.opts = opts = this.prepareOpts(opts);
691
692            this.id=opts.id;
693
694            // destroy if called on an existing component
695            if (opts.element.data("select2") !== undefined &&
696                opts.element.data("select2") !== null) {
697                opts.element.data("select2").destroy();
698            }
699
700            this.container = this.createContainer(opts);
701
702            this.liveRegion = $("<span>", {
703                    role: "status",
704                    "aria-live": "polite"
705                })
706                .addClass("select2-hidden-accessible")
707                .appendTo(document.body);
708
709            this.containerId="s2id_"+(opts.element.attr("id") || "autogen"+nextUid());
710            this.containerEventName= this.containerId
711                .replace(/([.])/g, '_')
712                .replace(/([;&,\-\.\+\*\~':"\!\^#$%@\[\]\(\)=>\|])/g, '\\$1');
713            this.container.attr("id", this.containerId);
714
715            this.container.attr("title", opts.element.attr("title"));
716
717            this.body = $("body");
718
719            syncCssClasses(this.container, this.opts.element, this.opts.adaptContainerCssClass);
720
721            this.container.attr("style", opts.element.attr("style"));
722            this.container.css(evaluate(opts.containerCss, this.opts.element));
723            this.container.addClass(evaluate(opts.containerCssClass, this.opts.element));
724
725            this.elementTabIndex = this.opts.element.attr("tabindex");
726
727            // swap container for the element
728            this.opts.element
729                .data("select2", this)
730                .attr("tabindex", "-1")
731                .before(this.container)
732                .on("click.select2", killEvent); // do not leak click events
733
734            this.container.data("select2", this);
735
736            this.dropdown = this.container.find(".select2-drop");
737
738            syncCssClasses(this.dropdown, this.opts.element, this.opts.adaptDropdownCssClass);
739
740            this.dropdown.addClass(evaluate(opts.dropdownCssClass, this.opts.element));
741            this.dropdown.data("select2", this);
742            this.dropdown.on("click", killEvent);
743
744            this.results = results = this.container.find(resultsSelector);
745            this.search = search = this.container.find("input.select2-input");
746
747            this.queryCount = 0;
748            this.resultsPage = 0;
749            this.context = null;
750
751            // initialize the container
752            this.initContainer();
753
754            this.container.on("click", killEvent);
755
756            installFilteredMouseMove(this.results);
757
758            this.dropdown.on("mousemove-filtered", resultsSelector, this.bind(this.highlightUnderEvent));
759            this.dropdown.on("touchstart touchmove touchend", resultsSelector, this.bind(function (event) {
760                this._touchEvent = true;
761                this.highlightUnderEvent(event);
762            }));
763            this.dropdown.on("touchmove", resultsSelector, this.bind(this.touchMoved));
764            this.dropdown.on("touchstart touchend", resultsSelector, this.bind(this.clearTouchMoved));
765
766            // Waiting for a click event on touch devices to select option and hide dropdown
767            // otherwise click will be triggered on an underlying element
768            this.dropdown.on('click', this.bind(function (event) {
769                if (this._touchEvent) {
770                    this._touchEvent = false;
771                    this.selectHighlighted();
772                }
773            }));
774
775            installDebouncedScroll(80, this.results);
776            this.dropdown.on("scroll-debounced", resultsSelector, this.bind(this.loadMoreIfNeeded));
777
778            // do not propagate change event from the search field out of the component
779            $(this.container).on("change", ".select2-input", function(e) {e.stopPropagation();});
780            $(this.dropdown).on("change", ".select2-input", function(e) {e.stopPropagation();});
781
782            // if jquery.mousewheel plugin is installed we can prevent out-of-bounds scrolling of results via mousewheel
783            if ($.fn.mousewheel) {
784                results.mousewheel(function (e, delta, deltaX, deltaY) {
785                    var top = results.scrollTop();
786                    if (deltaY > 0 && top - deltaY <= 0) {
787                        results.scrollTop(0);
788                        killEvent(e);
789                    } else if (deltaY < 0 && results.get(0).scrollHeight - results.scrollTop() + deltaY <= results.height()) {
790                        results.scrollTop(results.get(0).scrollHeight - results.height());
791                        killEvent(e);
792                    }
793                });
794            }
795
796            installKeyUpChangeEvent(search);
797            search.on("keyup-change input paste", this.bind(this.updateResults));
798            search.on("focus", function () { search.addClass("select2-focused"); });
799            search.on("blur", function () { search.removeClass("select2-focused");});
800
801            this.dropdown.on("mouseup", resultsSelector, this.bind(function (e) {
802                if ($(e.target).closest(".select2-result-selectable").length > 0) {
803                    this.highlightUnderEvent(e);
804                    this.selectHighlighted(e);
805                }
806            }));
807
808            // trap all mouse events from leaving the dropdown. sometimes there may be a modal that is listening
809            // for mouse events outside of itself so it can close itself. since the dropdown is now outside the select2's
810            // dom it will trigger the popup close, which is not what we want
811            // focusin can cause focus wars between modals and select2 since the dropdown is outside the modal.
812            this.dropdown.on("click mouseup mousedown touchstart touchend focusin", function (e) { e.stopPropagation(); });
813
814            this.nextSearchTerm = undefined;
815
816            if ($.isFunction(this.opts.initSelection)) {
817                // initialize selection based on the current value of the source element
818                this.initSelection();
819
820                // if the user has provided a function that can set selection based on the value of the source element
821                // we monitor the change event on the element and trigger it, allowing for two way synchronization
822                this.monitorSource();
823            }
824
825            if (opts.maximumInputLength !== null) {
826                this.search.attr("maxlength", opts.maximumInputLength);
827            }
828
829            var disabled = opts.element.prop("disabled");
830            if (disabled === undefined) disabled = false;
831            this.enable(!disabled);
832
833            var readonly = opts.element.prop("readonly");
834            if (readonly === undefined) readonly = false;
835            this.readonly(readonly);
836
837            // Calculate size of scrollbar
838            scrollBarDimensions = scrollBarDimensions || measureScrollbar();
839
840            this.autofocus = opts.element.prop("autofocus");
841            opts.element.prop("autofocus", false);
842            if (this.autofocus) this.focus();
843
844            this.search.attr("placeholder", opts.searchInputPlaceholder);
845        },
846
847        // abstract
848        destroy: function () {
849            var element=this.opts.element, select2 = element.data("select2"), self = this;
850
851            this.close();
852
853            if (element.length && element[0].detachEvent) {
854                element.each(function () {
855                    this.detachEvent("onpropertychange", self._sync);
856                });
857            }
858            if (this.propertyObserver) {
859                this.propertyObserver.disconnect();
860                this.propertyObserver = null;
861            }
862            this._sync = null;
863
864            if (select2 !== undefined) {
865                select2.container.remove();
866                select2.liveRegion.remove();
867                select2.dropdown.remove();
868                element
869                    .removeClass("select2-offscreen")
870                    .removeData("select2")
871                    .off(".select2")
872                    .prop("autofocus", this.autofocus || false);
873                if (this.elementTabIndex) {
874                    element.attr({tabindex: this.elementTabIndex});
875                } else {
876                    element.removeAttr("tabindex");
877                }
878                element.show();
879            }
880
881            cleanupJQueryElements.call(this,
882                "container",
883                "liveRegion",
884                "dropdown",
885                "results",
886                "search"
887            );
888        },
889
890        // abstract
891        optionToData: function(element) {
892            if (element.is("option")) {
893                return {
894                    id:element.prop("value"),
895                    text:element.text(),
896                    element: element.get(),
897                    css: element.attr("class"),
898                    disabled: element.prop("disabled"),
899                    locked: equal(element.attr("locked"), "locked") || equal(element.data("locked"), true)
900                };
901            } else if (element.is("optgroup")) {
902                return {
903                    text:element.attr("label"),
904                    children:[],
905                    element: element.get(),
906                    css: element.attr("class")
907                };
908            }
909        },
910
911        // abstract
912        prepareOpts: function (opts) {
913            var element, select, idKey, ajaxUrl, self = this;
914
915            element = opts.element;
916
917            if (element.get(0).tagName.toLowerCase() === "select") {
918                this.select = select = opts.element;
919            }
920
921            if (select) {
922                // these options are not allowed when attached to a select because they are picked up off the element itself
923                $.each(["id", "multiple", "ajax", "query", "createSearchChoice", "initSelection", "data", "tags"], function () {
924                    if (this in opts) {
925                        throw new Error("Option '" + this + "' is not allowed for Select2 when attached to a <select> element.");
926                    }
927                });
928            }
929
930            opts = $.extend({}, {
931                populateResults: function(container, results, query
931) {
932                    var populate, id=this.opts.id, liveRegion=this.liveRegion;
933
934                    populate=function(results, container, depth) {
935
936                        var i, l, result, selectable, disabled, compound, node, label, innerContainer, formatted;
937
938                        results = opts.sortResults(results, container, query);
939
940                        // collect the created nodes for bulk append
941                        var nodes = [];
942                        for (i = 0, l = results.length; i < l; i = i + 1) {
943
944                            result=results[i];
945
946                            disabled = (result.disabled === true);
947                            selectable = (!disabled) && (id(result) !== undefined);
948
949                            compound=result.children && result.children.length > 0;
950
951                            node=$("<li></li>");
952                            node.addClass("select2-results-dept-"+depth);
953                            node.addClass("select2-result");
954                            node.addClass(selectable ? "select2-result-selectable" : "select2-result-unselectable");
955                            if (disabled) { node.addClass("select2-disabled"); }
956                            if (compound) { node.addClass("select2-result-with-children"); }
957                            node.addClass(self.opts.formatResultCssClass(result));
958                            node.attr("role", "presentation");
959
960                            label=$(document.createElement("div"));
961                            label.addClass("select2-result-label");
962                            label.attr("id", "select2-result-label-" + nextUid());
963                            label.attr("role", "option");
964
965                            formatted=opts.formatResult(result, label, query, self.opts.escapeMarkup);
966                            if (formatted!==undefined) {
967                                label.html(formatted);
968                                node.append(label);
969                            }
970
971
972                            if (compound) {
973
974                                innerContainer=$("<ul></ul>");
975                                innerContainer.addClass("select2-result-sub");
976                                populate(result.children, innerContainer, depth+1);
977                                node.append(innerContainer);
978                            }
979
980                            node.data("select2-data", result);
981                            nodes.push(node[0]);
982                        }
983
984                        // bulk append the created nodes
985                        container.append(nodes);
986                        liveRegion.text(opts.formatMatches(results.length));
987                    };
988
989                    populate(results, container, 0);
990                }
991            }, $.fn.select2.defaults, opts);
992
993            if (typeof(opts.id) !== "function") {
994                idKey = opts.id;
995                opts.id = function (e) { return e[idKey]; };
996            }
997
998            if ($.isArray(opts.element.data("select2Tags"))) {
999                if ("tags" in opts) {
1000                    throw "tags specified as both an attribute 'data-select2-tags' and in options of Select2 " + opts.element.attr("id");
1001                }
1002                opts.tags=opts.element.data("select2Tags");
1003            }
1004
1005            if (select) {
1006                opts.query = this.bind(function (query) {
1007                    var data = { results: [], more: false },
1008                        term = query.term,
1009                        children, placeholderOption, process;
1010
1011                    process=function(element, collection) {
1012                        var group;
1013                        if (element.is("option")) {
1014                            if (query.matcher(term, element.text(), element)) {
1015                                collection.push(self.optionToData(element));
1016                            }
1017                        } else if (element.is("optgroup")) {
1018                            group=self.optionToData(element);
1019                            element.children().each2(function(i, elm) { process(elm, group.children); });
1020                            if (group.children.length>0) {
1021                                collection.push(group);
1022                            }
1023                        }
1024                    };
1025
1026                    children=element.children();
1027
1028                    // ignore the placeholder option if there is one
1029                    if (this.getPlaceholder() !== undefined && children.length > 0) {
1030                        placeholderOption = this.getPlaceholderOption();
1031                        if (placeholderOption) {
1032                            children=children.not(placeholderOption);
1033                        }
1034                    }
1035
1036                    children.each2(function(i, elm) { process(elm, data.results); });
1037
1038                    query.callback(data);
1039                });
1040                // this is needed because inside val() we construct choices from options and their id is hardcoded
1041                opts.id=function(e) { return e.id; };
1042            } else {
1043                if (!("query" in opts)) {
1044
1045                    if ("ajax" in opts) {
1046                        ajaxUrl = opts.element.data("ajax-url");
1047                        if (ajaxUrl && ajaxUrl.length > 0) {
1048                            opts.ajax.url = ajaxUrl;
1049                        }
1050                        opts.query = ajax.call(opts.element, opts.ajax);
1051                    } else if ("data" in opts) {
1052                        opts.query = local(opts.data);
1053                    } else if ("tags" in opts) {
1054                        opts.query = tags(opts.tags);
1055                        if (opts.createSearchChoice === undefined) {
1056                            opts.createSearchChoice = function (term) { return {id: $.trim(term), text: $.trim(term)}; };
1057                        }
1058                        if (opts.initSelection === undefined) {
1059                            opts.initSelection = function (element, callback) {
1060                                var data = [];
1061                                $(splitVal(element.val(), opts.separator)).each(function () {
1062                                    var obj = { id: this, text: this },
1063                                        tags = opts.tags;
1064                                    if ($.isFunction(tags)) tags=tags();
1065                                    $(tags).each(function() { if (equal(this.id, obj.id)) { obj = this; return false; } });
1066                                    data.push(obj);
1067                                });
1068
1069                                callback(data);
1070                            };
1071                        }
1072                    }
1073                }
1074            }
1075            if (typeof(opts.query) !== "function") {
1076                throw "query function not defined for Select2 " + opts.element.attr("id");
1077            }
1078
1079            if (opts.createSearchChoicePosition === 'top') {
1080                opts.createSearchChoicePosition = function(list, item) { list.unshift(item); };
1081            }
1082            else if (opts.createSearchChoicePosition === 'bottom') {
1083                opts.createSearchChoicePosition = function(list, item) { list.push(item); };
1084            }
1085            else if (typeof(opts.createSearchChoicePosition) !== "function")  {
1086                throw "invalid createSearchChoicePosition option must be 'top', 'bottom' or a custom function";
1087            }
1088
1089            return opts;
1090        },
1091
1092        /**
1093         * Monitor the original element for changes and update select2 accordingly
1094         */
1095        // abstract
1096        monitorSource: function () {
1097            var el = this.opts.element, observer, self = this;
1098
1099            el.on("change.select2", this.bind(function (e) {
1100                if (this.opts.element.data("select2-change-triggered") !== true) {
1101                    this.initSelection();
1102                }
1103            }));
1104
1105            this._sync = this.bind(function () {
1106
1107                // sync enabled state
1108                var disabled = el.prop("disabled");
1109                if (disabled === undefined) disabled = false;
1110                this.enable(!disabled);
1111
1112                var readonly = el.prop("readonly");
1113                if (readonly === undefined) readonly = false;
1114                this.readonly(readonly);
1115
1116                syncCssClasses(this.container, this.opts.element, this.opts.adaptContainerCssClass);
1117                this.container.addClass(evaluate(this.opts.containerCssClass, this.opts.element));
1118
1119                syncCssClasses(this.dropdown, this.opts.element, this.opts.adaptDropdownCssClass);
1120                this.dropdown.addClass(evaluate(this.opts.dropdownCssClass, this.opts.element));
1121
1122            });
1123
1124            // IE8-10 (IE9/10 won't fire propertyChange via attachEventListener)
1125            if (el.length && el[0].attachEvent) {
1126                el.each(function() {
1127                    this.attachEvent("onpropertychange", self._sync);
1128                });
1129            }
1130
1131            // safari, chrome, firefox, IE11
1132            observer = window.MutationObserver || window.WebKitMutationObserver|| window.MozMutationObserver;
1133            if (observer !== undefined) {
1134                if (this.propertyObserver) { delete this.propertyObserver; this.propertyObserver = null; }
1135                this.propertyObserver = new observer(function (mutations) {
1136                    $.each(mutations, self._sync);
1137                });
1138                this.propertyObserver.observe(el.get(0), { attributes:true, subtree:false });
1139            }
1140        },
1141
1142        // abstract
1143        triggerSelect: function(data) {
1144            var evt = $.Event("select2-selecting", { val: this.id(data), object: data, choice: data });
1145            this.opts.element.trigger(evt);
1146            return !evt.isDefaultPrevented();
1147        },
1148
1149        /**
1150         * Triggers the change event on the source element
1151         */
1152        // abstract
1153        triggerChange: function (details) {
1154
1155            details = details || {};
1156            details= $.extend({}, details, { type: "change", val: this.val() });
1157            // prevents recursive triggering
1158            this.opts.element.data("select2-change-triggered", true);
1159            this.opts.element.trigger(details);
1160            this.opts.element.data("select2-change-triggered", false);
1161
1162            // some validation frameworks ignore the change event and listen instead to keyup, click for selects
1163            // so here we trigger the click event manually
1164            this.opts.element.click();
1165
1166            // ValidationEngine ignores the change event and listens instead to blur
1167            // so here we trigger the blur event manually if so desired
1168            if (this.opts.blurOnChange)
1169                this.opts.element.blur();
1170        },
1171
1172        //abstract
1173        isInterfaceEnabled: function()
1174        {
1175            return this.enabledInterface === true;
1176        },
1177
1178        // abstract
1179        enableInterface: function() {
1180            var enabled = this._enabled && !this._readonly,
1181                disabled = !enabled;
1182
1183            if (enabled === this.enabledInterface) return false;
1184
1185            this.container.toggleClass("select2-container-disabled", disabled);
1186            this.close();
1187            this.enabledInterface = enabled;
1188
1189            return true;
1190        },
1191
1192        // abstract
1193        enable: function(enabled) {
1194            if (enabled === undefined) enabled = true;
1195            if (this._enabled === enabled) return;
1196            this._enabled = enabled;
1197
1198            this.opts.element.prop("disabled", !enabled);
1199            this.enableInterface();
1200        },
1201
1202        // abstract
1203        disable: function() {
1204            this.enable(false);
1205        },
1206
1207        // abstract
1208        readonly: function(enabled) {
1209            if (enabled === undefined) enabled = false;
1210            if (this._readonly === enabled) return;
1211            this._readonly = enabled;
1212
1213            this.opts.element.prop("readonly", enabled);
1214            this.enableInterface();
1215        },
1216
1217        // abstract
1218        opened: function () {
1219            return (this.container) ? this.container.hasClass("select2-dropdown-open") : false;
1220        },
1221
1222        // abstract
1223        positionDropdown: function() {
1224            var $dropdown = this.dropdown,
1225                offset = this.container.offset(),
1226                height = this.container.outerHeight(false),
1227                width = this.container.outerWidth(false),
1228                dropHeight = $dropdown.outerHeight(false),
1229                $window = $(window),
1230                windowWidth = $window.width(),
1231                windowHeight = $window.height(),
1232                viewPortRight = $window.scrollLeft() + windowWidth,
1233                viewportBottom = $window.scrollTop() + windowHeight,
1234                dropTop = offset.top + height,
1235                dropLeft = offset.left,
1236                enoughRoomBelow = dropTop + dropHeight <= viewportBottom,
1237                enoughRoomAbove = (offset.top - dropHeight) >= $window.scrollTop(),
1238                dropWidth = $dropdown.outerWidth(false),
1239                enoughRoomOnRight = dropLeft + dropWidth <= viewPortRight,
1240                aboveNow = $dropdown.hasClass("select2-drop-above"),
1241                bodyOffset,
1242                above,
1243                changeDirection,
1244                css,
1245                resultsListNode;
1246
1247            // always prefer the current above/below alignment, unless there is not enough room
1248            if (aboveNow) {
1249                above = true;
1250                if (!enoughRoomAbove && enoughRoomBelow) {
1251                    changeDirection = true;
1252                    above = false;
1253                }
1254            } else {
1255                above = false;
1256                if (!enoughRoomBelow && enoughRoomAbove) {
1257                    changeDirection = true;
1258                    above = true;
1259                }
1260            }
1261
1262            //if we are changing direction we need to get positions when dropdown is hidden;
1263            if (changeDirection) {
1264                $dropdown.hide();
1265                offset = this.container.offset();
1266                height = this.container.outerHeight(false);
1267                width = this.container.outerWidth(false);
1268                dropHeight = $dropdown.outerHeight(false);
1269                viewPortRight = $window.scrollLeft() + windowWidth;
1270                viewportBottom = $window.scrollTop() + windowHeight;
1271                dropTop = offset.top + height;
1272                dropLeft = offset.left;
1273                dropWidth = $dropdown.outerWidth(false);
1274                enoughRoomOnRight = dropLeft + dropWidth <= viewPortRight;
1275                $dropdown.show();
1276
1277                // fix so the cursor does not move to the left within the search-textbox in IE
1278                this.focusSearch();
1279            }
1280
1281            if (this.opts.dropdownAutoWidth) {
1282                resultsListNode = $('.select2-results', $dropdown)[0];
1283                $dropdown.addClass('select2-drop-auto-width');
1284                $dropdown.css('width', '');
1285                // Add scrollbar width to dropdown if vertical scrollbar is present
1286                dropWidth = $dropdown.outerWidth(false) + (resultsListNode.scrollHeight === resultsListNode.clientHeight ? 0 : scrollBarDimensions.width);
1287                dropWidth > width ? width = dropWidth : dropWidth = width;
1288                dropHeight = $dropdown.outerHeight(false);
1289                enoughRoomOnRight = dropLeft + dropWidth <= viewPortRight;
1290            }
1291            else {
1292                this.container.removeClass('select2-drop-auto-width');
1293            }
1294
1295            //console.log("below/ droptop:", dropTop, "dropHeight", dropHeight, "sum", (dropTop+dropHeight)+" viewport bottom", viewportBottom, "enough?", enoughRoomBelow);
1296            //console.log("above/ offset.top", offset.top, "dropHeight", dropHeight, "top", (offset.top-dropHeight), "scrollTop", this.body.scrollTop(), "enough?", enoughRoomAbove);
1297
1298            // fix positioning when body has an offset and is not position: static
1299            if (this.body.css('position') !== 'static') {
1300                bodyOffset = this.body.offset();
1301                dropTop -= bodyOffset.top;
1302                dropLeft -= bodyOffset.left;
1303            }
1304
1305            if (!enoughRoomOnRight) {
1306                dropLeft = offset.left + this.container.outerWidth(false) - dropWidth;
1307            }
1308
1309            css =  {
1310                left: dropLeft,
1311                width: width
1312            };
1313
1314            if (above) {
1315                css.top = offset.top - dropHeight;
1316                css.bottom = 'auto';
1317                this.container.addClass("select2-drop-above");
1318                $dropdown.addClass("select2-drop-above");
1319            }
1320            else {
1321                css.top = dropTop;
1322                css.bottom = 'auto';
1323                this.container.removeClass("select2-drop-above");
1324                $dropdown.removeClass("select2-drop-above");
1325            }
1326            css = $.extend(css, evaluate(this.opts.dropdownCss, this.opts.element));
1327
1328            $dropdown.css(css);
1329        },
1330
1331        // abstract
1332        shouldOpen: function() {
1333            var event;
1334
1335            if (this.opened()) return false;
1336
1337            if (this._enabled === false || this._readonly === true) return false;
1338
1339            event = $.Event("select2-opening");
1340            this.opts.element.trigger(event);
1341            return !event.isDefaultPrevented();
1342        },
1343
1344        // abstract
1345        clearDropdownAlignmentPreference: function() {
1346            // clear the classes used to figure out the preference of where the dropdown should be opened
1347            this.container.removeClass("select2-drop-above");
1348            this.dropdown.removeClass("select2-drop-above");
1349        },
1350
1351        /**
1352         * Opens the dropdown
1353         *
1354         * @return {Boolean} whether or not dropdown was opened. This method will return false if, for example,
1355         * the dropdown is already open, or if the 'open' event listener on the element called preventDefault().
1356         */
1357        // abstract
1358        open: function () {
1359
1360            if (!this.shouldOpen()) return false;
1361
1362            this.opening();
1363
1364            // Only bind the document mousemove when the dropdown is visible
1365            $document.on("mousemove.select2Event", function (e) {
1366                lastMousePosition.x = e.pageX;
1367                lastMousePosition.y = e.pageY;
1368            });
1369
1370            return true;
1371        },
1372
1373        /**
1374         * Performs the opening of the dropdown
1375         */
1376        // abstract
1377        opening: function() {
1378            var cid = this.containerEventName,
1379                scroll = "scroll." + cid,
1380                resize = "resize."+cid,
1381                orient = "orientationchange."+cid,
1382                mask;
1383
1384            this.container.addClass("select2-dropdown-open").addClass("select2-container-active");
1385
1386            this.clearDropdownAlignmentPreference();
1387
1388            if(this.dropdown[0] !== this.body.children().last()[0]) {
1389                this.dropdown.detach().appendTo(this.body);
1390            }
1391
1392            // create the dropdown mask if doesn't already exist
1393            mask = $("#select2-drop-mask");
1394            if (mask.length == 0) {
1395                mask = $(document.createElement("div"));
1396                mask.attr("id","select2-drop-mask").attr("class","select2-drop-mask");
1397                mask.hide();
1398                mask.appendTo(this.body);
1399                mask.on("mousedown touchstart click", function (e) {
1400                    // Prevent IE from generating a click event on the body
1401                    reinsertElement(mask);
1402
1403                    var dropdown = $("#select2-drop"), self;
1404                    if (dropdown.length > 0) {
1405                        self=dropdown.data("select2");
1406                        if (self.opts.selectOnBlur) {
1407                            self.selectHighlighted({noFocus: true});
1408                        }
1409                        self.close();
1410                        e.preventDefault();
1411                        e.stopPropagation();
1412                    }
1413                });
1414            }
1415
1416            // ensure the mask is always right before the dropdown
1417            if (this.dropdown.prev()[0] !== mask[0]) {
1418                this.dropdown.before(mask);
1419            }
1420
1421            // move the global id to the correct dropdown
1422            $("#select2-drop").removeAttr("id");
1423            this.dropdown.attr("id", "select2-drop");
1424
1425            // show the elements
1426            mask.show();
1427
1428            this.positionDropdown();
1429            this.dropdown.show();
1430            this.positionDropdown();
1431
1432            this.dropdown.addClass("select2-drop-active");
1433
1434            // attach listeners to events that can change the position of the container and thus require
1435            // the position of the dropdown to be updated as well so it does not come unglued from the container
1436            var that = this;
1437            this.container.parents().add(window).each(function () {
1438                $(this).on(resize+" "+scroll+" "+orient, function (e) {
1439                    if (that.opened()) that.positionDropdown();
1440                });
1441            });
1442
1443
1444        },
1445
1446        // abstract
1447        close: function () {
1448            if (!this.opened()) return;
1449
1450            var cid = this.containerEventName,
1451                scroll = "scroll." + cid,
1452                resize = "resize."+cid,
1453                orient = "orientationchange."+cid;
1454
1455            // unbind event listeners
1456            this.container.parents().add(window).each(function () { $(this).off(scroll).off(resize).off(orient); });
1457
1458            this.clearDropdownAlignmentPreference();
1459
1460            $("#select2-drop-mask").hide();
1461            this.dropdown.removeAttr("id"); // only the active dropdown has the select2-drop id
1462            this.dropdown.hide();
1463            this.container.removeClass("select2-dropdown-open").removeClass("select2-container-active");
1464            this.results.empty();
1465
1466            // Now that the dropdown is closed, unbind the global document mousemove event
1467            $document.off("mousemove.select2Event");
1468
1469            this.clearSearch();
1470            this.search.removeClass("select2-active");
1471            this.opts.element.trigger($.Event("select2-close"));
1472        },
1473
1474        /**
1475         * Opens control, sets input value, and updates results.
1476         */
1477        // abstract
1478        externalSearch: function (term) {
1479            this.open();
1480            this.search.val(term);
1481            this.updateResults(false);
1482        },
1483
1484        // abstract
1485        clearSearch: function () {
1486
1487        },
1488
1489        //abstract
1490        getMaximumSelectionSize: function() {
1491            return evaluate(this.opts.maximumSelectionSize, this.opts.element);
1492        },
1493
1494        // abstract
1495        ensureHighlightVisible: function () {
1496            var results = this.results, children, index, child, hb, rb, y, more, topOffset;
1497
1498            index = this.highlight();
1499
1500            if (index < 0) return;
1501
1502            if (index == 0) {
1503
1504                // if the first element is highlighted scroll all the way to the top,
1505                // that way any unselectable headers above it will also be scrolled
1506                // into view
1507
1508                results.scrollTop(0);
1509                return;
1510            }
1511
1512            children = this.findHighlightableChoices().find('.select2-result-label');
1513
1514            child = $(children[index]);
1515
1516            topOffset = (child.offset() || {}).top || 0;
1517
1518            hb = topOffset + child.outerHeight(true);
1519
1520            // if this is the last child lets also make sure select2-more-results is visible
1521            if (index === children.length - 1) {
1522                more = results.find("li.select2-more-results");
1523                if (more.length > 0) {
1524                    hb = more.offset().top + more.outerHeight(true);
1525                }
1526            }
1527
1528            rb = results.offset().top + results.outerHeight(true);
1529            if (hb > rb) {
1530                results.scrollTop(results.scrollTop() + (hb - rb));
1531            }
1532            y = topOffset - results.offset().top;
1533
1534            // make sure the top of the element is visible
1535            if (y < 0 && child.css('display') != 'none' ) {
1536                results.scrollTop(results.scrollTop() + y); // y is negative
1537            }
1538        },
1539
1540        // abstract
1541        findHighlightableChoices: function() {
1542            return this.results.find(".select2-result-selectable:not(.select2-disabled):not(.select2-selected)");
1543        },
1544
1545        // abstract
1546        moveHighlight: function (delta) {
1547            var choices = this.findHighlightableChoices(),
1548                index = this.highlight();
1549
1550            while (index > -1 && index < choices.length) {
1551                index += delta;
1552                var choice = $(choices[index]);
1553                if (choice.hasClass("select2-result-selectable") && !c
1553hoice.hasClass("select2-disabled") && !choice.hasClass("select2-selected")) {
1554                    this.highlight(index);
1555                    break;
1556                }
1557            }
1558        },
1559
1560        // abstract
1561        highlight: function (index) {
1562            var choices = this.findHighlightableChoices(),
1563                choice,
1564                data;
1565
1566            if (arguments.length === 0) {
1567                return indexOf(choices.filter(".select2-highlighted")[0], choices.get());
1568            }
1569
1570            if (index >= choices.length) index = choices.length - 1;
1571            if (index < 0) index = 0;
1572
1573            this.removeHighlight();
1574
1575            choice = $(choices[index]);
1576            choice.addClass("select2-highlighted");
1577
1578            // ensure assistive technology can determine the active choice
1579            this.search.attr("aria-activedescendant", choice.find(".select2-result-label").attr("id"));
1580
1581            this.ensureHighlightVisible();
1582
1583            this.liveRegion.text(choice.text());
1584
1585            data = choice.data("select2-data");
1586            if (data) {
1587                this.opts.element.trigger({ type: "select2-highlight", val: this.id(data), choice: data });
1588            }
1589        },
1590
1591        removeHighlight: function() {
1592            this.results.find(".select2-highlighted").removeClass("select2-highlighted");
1593        },
1594
1595        touchMoved: function() {
1596            this._touchMoved = true;
1597        },
1598
1599        clearTouchMoved: function() {
1600          this._touchMoved = false;
1601        },
1602
1603        // abstract
1604        countSelectableResults: function() {
1605            return this.findHighlightableChoices().length;
1606        },
1607
1608        // abstract
1609        highlightUnderEvent: function (event) {
1610            var el = $(event.target).closest(".select2-result-selectable");
1611            if (el.length > 0 && !el.is(".select2-highlighted")) {
1612                var choices = this.findHighlightableChoices();
1613                this.highlight(choices.index(el));
1614            } else if (el.length == 0) {
1615                // if we are over an unselectable item remove all highlights
1616                this.removeHighlight();
1617            }
1618        },
1619
1620        // abstract
1621        loadMoreIfNeeded: function () {
1622            var results = this.results,
1623                more = results.find("li.select2-more-results"),
1624                below, // pixels the element is below the scroll fold, below==0 is when the element is starting to be visible
1625                page = this.resultsPage + 1,
1626                self=this,
1627                term=this.search.val(),
1628                context=this.context;
1629
1630            if (more.length === 0) return;
1631            below = more.offset().top - results.offset().top - results.height();
1632
1633            if (below <= this.opts.loadMorePadding) {
1634                more.addClass("select2-active");
1635                this.opts.query({
1636                        element: this.opts.element,
1637                        term: term,
1638                        page: page,
1639                        context: context,
1640                        matcher: this.opts.matcher,
1641                        callback: this.bind(function (data) {
1642
1643                    // ignore a response if the select2 has been closed before it was received
1644                    if (!self.opened()) return;
1645
1646
1647                    self.opts.populateResults.call(this, results, data.results, {term: term, page: page, context:context});
1648                    self.postprocessResults(data, false, false);
1649
1650                    if (data.more===true) {
1651                        more.detach().appendTo(results).text(evaluate(self.opts.formatLoadMore, self.opts.element, page+1));
1652                        window.setTimeout(function() { self.loadMoreIfNeeded(); }, 10);
1653                    } else {
1654                        more.remove();
1655                    }
1656                    self.positionDropdown();
1657                    self.resultsPage = page;
1658                    self.context = data.context;
1659                    this.opts.element.trigger({ type: "select2-loaded", items: data });
1660                })});
1661            }
1662        },
1663
1664        /**
1665         * Default tokenizer function which does nothing
1666         */
1667        tokenize: function() {
1668
1669        },
1670
1671        /**
1672         * @param initial whether or not this is the call to this method right after the dropdown has been opened
1673         */
1674        // abstract
1675        updateResults: function (initial) {
1676            var search = this.search,
1677                results = this.results,
1678                opts = this.opts,
1679                data,
1680                self = this,
1681                input,
1682                term = search.val(),
1683                lastTerm = $.data(this.container, "select2-last-term"),
1684                // sequence number used to drop out-of-order responses
1685                queryNumber;
1686
1687            // prevent duplicate queries against the same term
1688            if (initial !== true && lastTerm && equal(term, lastTerm)) return;
1689
1690            $.data(this.container, "select2-last-term", term);
1691
1692            // if the search is currently hidden we do not alter the results
1693            if (initial !== true && (this.showSearchInput === false || !this.opened())) {
1694                return;
1695            }
1696
1697            function postRender() {
1698                search.removeClass("select2-active");
1699                self.positionDropdown();
1700                if (results.find('.select2-no-results,.select2-selection-limit,.select2-searching').length) {
1701                    self.liveRegion.text(results.text());
1702                }
1703                else {
1704                    self.liveRegion.text(self.opts.formatMatches(results.find('.select2-result-selectable').length));
1705                }
1706            }
1707
1708            function render(html) {
1709                results.html(html);
1710                postRender();
1711            }
1712
1713            queryNumber = ++this.queryCount;
1714
1715            var maxSelSize = this.getMaximumSelectionSize();
1716            if (maxSelSize >=1) {
1717                data = this.data();
1718                if ($.isArray(data) && data.length >= maxSelSize && checkFormatter(opts.formatSelectionTooBig, "formatSelectionTooBig")) {
1719                    render("<li class='select2-selection-limit'>" + evaluate(opts.formatSelectionTooBig, opts.element, maxSelSize) + "</li>");
1720                    return;
1721                }
1722            }
1723
1724            if (search.val().length < opts.minimumInputLength) {
1725                if (checkFormatter(opts.formatInputTooShort, "formatInputTooShort")) {
1726                    render("<li class='select2-no-results'>" + evaluate(opts.formatInputTooShort, opts.element, search.val(), opts.minimumInputLength) + "</li>");
1727                } else {
1728                    render("");
1729                }
1730                if (initial && this.showSearch) this.showSearch(true);
1731                return;
1732            }
1733
1734            if (opts.maximumInputLength && search.val().length > opts.maximumInputLength) {
1735                if (checkFormatter(opts.formatInputTooLong, "formatInputTooLong")) {
1736                    render("<li class='select2-no-results'>" + evaluate(opts.formatInputTooLong, opts.element, search.val(), opts.maximumInputLength) + "</li>");
1737                } else {
1738                    render("");
1739                }
1740                return;
1741            }
1742
1743            if (opts.formatSearching && this.findHighlightableChoices().length === 0) {
1744                render("<li class='select2-searching'>" + evaluate(opts.formatSearching, opts.element) + "</li>");
1745            }
1746
1747            search.addClass("select2-active");
1748
1749            this.removeHighlight();
1750
1751            // give the tokenizer a chance to pre-process the input
1752            input = this.tokenize();
1753            if (input != undefined && input != null) {
1754                search.val(input);
1755            }
1756
1757            this.resultsPage = 1;
1758
1759            opts.query({
1760                element: opts.element,
1761                    term: search.val(),
1762                    page: this.resultsPage,
1763                    context: null,
1764                    matcher: opts.matcher,
1765                    callback: this.bind(function (data) {
1766                var def; // default choice
1767
1768                // ignore old responses
1769                if (queryNumber != this.queryCount) {
1770                  return;
1771                }
1772
1773                // ignore a response if the select2 has been closed before it was received
1774                if (!this.opened()) {
1775                    this.search.removeClass("select2-active");
1776                    return;
1777                }
1778
1779                // handle ajax error
1780                if(data.hasError !== undefined && checkFormatter(opts.formatAjaxError, "formatAjaxError")) {
1781                    render("<li class='select2-ajax-error'>" + evaluate(opts.formatAjaxError, opts.element, data.jqXHR, data.textStatus, data.errorThrown) + "</li>");
1782                    return;
1783                }
1784
1785                // save context, if any
1786                this.context = (data.context===undefined) ? null : data.context;
1787                // create a default choice and prepend it to the list
1788                if (this.opts.createSearchChoice && search.val() !== "") {
1789                    def = this.opts.createSearchChoice.call(self, search.val(), data.results);
1790                    if (def !== undefined && def !== null && self.id(def) !== undefined && self.id(def) !== null) {
1791                        if ($(data.results).filter(
1792                            function () {
1793                                return equal(self.id(this), self.id(def));
1794                            }).length === 0) {
1795                            this.opts.createSearchChoicePosition(data.results, def);
1796                        }
1797                    }
1798                }
1799
1800                if (data.results.length === 0 && checkFormatter(opts.formatNoMatches, "formatNoMatches")) {
1801                    render("<li class='select2-no-results'>" + evaluate(opts.formatNoMatches, opts.element, search.val()) + "</li>");
1802                    return;
1803                }
1804
1805                results.empty();
1806                self.opts.populateResults.call(this, results, data.results, {term: search.val(), page: this.resultsPage, context:null});
1807
1808                if (data.more === true && checkFormatter(opts.formatLoadMore, "formatLoadMore")) {
1809                    results.append("<li class='select2-more-results'>" + opts.escapeMarkup(evaluate(opts.formatLoadMore, opts.element, this.resultsPage)) + "</li>");
1810                    window.setTimeout(function() { self.loadMoreIfNeeded(); }, 10);
1811                }
1812
1813                this.postprocessResults(data, initial);
1814
1815                postRender();
1816
1817                this.opts.element.trigger({ type: "select2-loaded", items: data });
1818            })});
1819        },
1820
1821        // abstract
1822        cancel: function () {
1823            this.close();
1824        },
1825
1826        // abstract
1827        blur: function () {
1828            // if selectOnBlur == true, select the currently highlighted option
1829            if (this.opts.selectOnBlur)
1830                this.selectHighlighted({noFocus: true});
1831
1832            this.close();
1833            this.container.removeClass("select2-container-active");
1834            // synonymous to .is(':focus'), which is available in jquery >= 1.6
1835            if (this.search[0] === document.activeElement) { this.search.blur(); }
1836            this.clearSearch();
1837            this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus");
1838        },
1839
1840        // abstract
1841        focusSearch: function () {
1842            focus(this.search);
1843        },
1844
1845        // abstract
1846        selectHighlighted: function (options) {
1847            if (this._touchMoved) {
1848              this.clearTouchMoved();
1849              return;
1850            }
1851            var index=this.highlight(),
1852                highlighted=this.results.find(".select2-highlighted"),
1853                data = highlighted.closest('.select2-result').data("select2-data");
1854			
1855            if (data) {
1856                this.highlight(index);
1857                this.onSelect(data, options);
1858            } else if (options && options.noFocus) {
1859                this.close();
1860			}
1861        },
1862
1863        // abstract
1864        getPlaceholder: function () {
1865            var placeholderOption;
1866            return this.opts.element.attr("placeholder") ||
1867                this.opts.element.attr("data-placeholder") || // jquery 1.4 compat
1868                this.opts.element.data("placeholder") ||
1869                this.opts.placeholder ||
1870                ((placeholderOption = this.getPlaceholderOption()) !== undefined ? placeholderOption.text() : undefined);
1871        },
1872
1873        // abstract
1874        getPlaceholderOption: function() {
1875            if (this.select) {
1876                var firstOption = this.select.children('option').first();
1877                if (this.opts.placeholderOption !== undefined ) {
1878                    //Determine the placeholder option based on the specified placeholderOption setting
1879                    return (this.opts.placeholderOption === "first" && firstOption) ||
1880                           (typeof this.opts.placeholderOption === "function" && this.opts.placeholderOption(this.select));
1881                } else if ($.trim(firstOption.text()) === "" && firstOption.val() === "") {
1882                    //No explicit placeholder option specified, use the first if it's blank
1883                    return firstOption;
1884                }
1885            }
1886        },
1887
1888        /**
1889         * Get the desired width for the container element.  This is
1890         * derived first from option `width` passed to select2, then
1891         * the inline 'style' on the original element, and finally
1892         * falls back to the jQuery calculated element width.
1893         */
1894        // abstract
1895        initContainerWidth: function () {
1896            function resolveContainerWidth() {
1897                var style, attrs, matches, i, l, attr;
1898
1899                if (this.opts.width === "off") {
1900                    return null;
1901                } else if (this.opts.width === "element"){
1902                    return this.opts.element.outerWidth(false) === 0 ? 'auto' : this.opts.element.outerWidth(false) + 'px';
1903                } else if (this.opts.width === "copy" || this.opts.width === "resolve") {
1904                    // check if there is inline style on the element that contains width
1905                    style = this.opts.element.attr('style');
1906                    if (style !== undefined) {
1907                        attrs = style.split(';');
1908                        for (i = 0, l = attrs.length; i < l; i = i + 1) {
1909                            attr = attrs[i].replace(/\s/g, '');
1910                            matches = attr.match(/^width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/i);
1911                            if (matches !== null && matches.length >= 1)
1912                                return matches[1];
1913                        }
1914                    }
1915
1916                    if (this.opts.width === "resolve") {
1917                        // next check if css('width') can resolve a width that is percent based, this is sometimes possible
1918                        // when attached to input type=hidden or elements hidden via css
1919                        style = this.opts.element.css('width');
1920                        if (style.indexOf("%") > 0) return style;
1921
1922                        // finally, fallback on the calculated width of the element
1923                        return (this.opts.element.outerWidth(false) === 0 ? 'auto' : this.opts.element.outerWidth(false) + 'px');
1924                    }
1925
1926                    return null;
1927                } else if ($.isFunction(this.opts.width)) {
1928                    return this.opts.width();
1929                } else {
1930                    return this.opts.width;
1931               }
1932            };
1933
1934            var width = resolveContainerWidth.call(this);
1935            if (width !== null) {
1936                this.container.css("width", width);
1937            }
1938        }
1939    });
1940
1941    SingleSelect2 = clazz(AbstractSelect2, {
1942
1943        // single
1944
1945        createContainer: function (opts) {
1946			var html = [
1947                "<a href='javascript:void(0)' class='select2-choice' tabindex='-1'>",
1948                "   <span class='select2-chosen'>&#160;</span><abbr class='select2-search-choice-close'></abbr>",
1949                "   <span class='select2-arrow' role='presentation'><b role='presentation'></b></span>",
1950                "</a>",
1951                "<label for='' class='select2-offscreen'></label>",
1952                "<input class='select2-focusser select2-offscreen' type='text' aria-haspopup='true' role='button' />",
1953                "<div class='select2-drop select2-display-none'>"];
1954			
1955			
1956            if (opts.searchBar) {
1957				var html2 = ["   <div class='select2-search'>"];
1958			} else {
1959				var html2 = ["   <div class='select2-search' style='display:none;'>"];
1960			}
1961			html = $.merge(html, html2);
1962				
1963			var html2 = [
1964				"       <label for='' class='select2-offscreen'></label>",
1965				"       <input type='text' autocomplete='off' autocorrect='off' autocapitalize='off' spellcheck='false' class='select2-input' role='combobox' aria-expanded='true'",
1966				"       aria-autocomplete='list' />",
1967				"   </div>",
1968				"   <ul class='select2-results' role='listbox'>",
1969				"   </ul>",
1970				"</div>"];
1971			
1972			html = $.merge(html, html2);
1973			
1974            var container = $(document.createElement("div")).attr({
1975                "class": "select2-container"
1976            }).html(html.join(""));
1977            return container;
1978        },
1979
1980        // single
1981        enableInterface: function() {
1982            if (this.parent.enableInterface.apply(this, arguments)) {
1983                this.focusser.prop("disabled", !this.isInterfaceEnabled());
1984            }
1985        },
1986
1987        // single
1988        opening: function () {
1989            var el, range, len;
1990
1991            if (this.opts.minimumResultsForSearch >= 0) {
1992                this.showSearch(true);
1993            }
1994
1995            this.parent.opening.apply(this, arguments);
1996
1997            if (this.showSearchInput !== false) {
1998                // IE appends focusser.val() at the end of field :/ so we manually insert it at the beginning using a range
1999                // all other browsers handle this just fine
2000
2001                this.search.val(this.focusser.val());
2002            }
2003            if (this.opts.shouldFocusInput(this)) {
2004                this.search.focus();
2005                // move the cursor to the end after focussing, otherwise it will be at the beginning and
2006                // new text will appear *before* focusser.val()
2007                el = this.search.get(0);
2008                if (el.createTextRange) {
2009                    range = el.createTextRange();
2010                    range.collapse(false);
2011                    range.select();
2012                } else if (el.setSelectionRange) {
2013                    len = this.search.val().length;
2014                    el.setSelectionRange(len, len);
2015                }
2016            }
2017
2018            // initializes search's value with nextSearchTerm (if defined by user)
2019            // ignore nextSearchTerm if the dropdown is opened by the user pressing a letter
2020            if(this.search.val() === "") {
2021                if(this.nextSearchTerm != undefined){
2022                    this.search.val(this.nextSearchTerm);
2023                    this.search.select();
2024                }
2025            }
2026
2027            this.focusser.prop("disabled", true).val("");
2028            this.updateResults(true);
2029            this.opts.element.trigger($.Event("select2-open"));
2030        },
2031
2032        // single
2033        close: function () {
2034            if (!this.opened()) return;
2035            this.parent.close.apply(this, arguments);
2036
2037            this.focusser.prop("disabled", false);
2038
2039            if (this.opts.shouldFocusInput(this)) {
2040                this.focusser.focus();
2041            }
2042        },
2043
2044        // single
2045        focus: function () {
2046            if (this.opened()) {
2047                this.close();
2048            } else {
2049                this.focusser.prop("disabled", false);
2050                if (this.opts.shouldFocusInput(this)) {
2051                    this.focusser.focus();
2052                }
2053            }
2054        },
2055
2056        // single
2057        isFocused: function () {
2058            return this.container.hasClass("select2-container-active");
2059        },
2060
2061        // single
2062        cancel: function () {
2063            this.parent.cancel.apply(this, arguments);
2064            this.focusser.prop("disabled", false);
2065
2066            if (this.opts.shouldFocusInput(this)) {
2067                this.focusser.focus();
2068            }
2069        },
2070
2071        // single
2072        destroy: function() {
2073            $("label[for='" + this.focusser.attr('id') + "']")
2074                .attr('for', this.opts.element.attr("id"));
2075            this.parent.destroy.apply(this, arguments);
2076
2077            cleanupJQueryElements.call(this,
2078                "selection",
2079                "focusser"
2080            );
2081        },
2082
2083        // single
2084        initContainer: function () {
2085
2086            var selection,
2087                container = this.container,
2088                dropdown = this.dropdown,
2089                idSuffix = nextUid(),
2090                elementLabel;
2091
2092            if (this.opts.minimumResultsForSearch < 0) {
2093                this.showSearch(false);
2094            } else {
2095                this.showSearch(true);
2096            }
2097
2098            this.selection = selection = container.find(".select2-choice");
2099
2100            this.focusser = container.find(".select2-focusser");
2101
2102            // add aria associations
2103            selection.find(".select2-chosen").attr("id", "select2-chosen-"+idSuffix);
2104            this.focusser.attr("aria-labelledby", "select2-chosen-"+idSuffix);
2105            this.results.attr("id", "select2-results-"+idSuffix);
2106            this.search.attr("aria-owns", "select2-results-"+idSuffix);
2107
2108            // rewrite labels from original element to focusser
2109            this.focusser.attr("id", "s2id_autogen"+idSuffix);
2110
2111            elementLabel = $("label[for='" + this.opts.element.attr("id") + "']");
2112
2113            this.focusser.prev()
2114                .text(elementLabel.text())
2115                .attr('for', this.focusser.attr('id'));
2116
2117            // Ensure the original element retains an accessible name
2118            var originalTitle = this.opts.element.attr("title");
2119            this.opts.element.attr("title", (originalTitle || elementLabel.text()));
2120
2121            this.focusser.attr("tabindex", this.elementTabIndex);
2122
2123            // write label for search field using the label from the focusser element
2124            this.search.attr("id", this.focusser.attr('id') + '_search');
2125
2126            this.search.prev()
2127                .text($("label[for='" + this.focusser.attr('id') + "']").text())
2128                .attr('for', this.search.attr('id'));
2129
2130            this.search.on("keydown", this.bind(function (e) {
2131                if (!this.isInterfaceEnabled()) return;
2132
2133                // filter 229 keyCodes (input method editor is processing key input)
2134                if (229 == e.keyCode) return;
2135
2136                if (e.which === KEY.PAGE_UP || e.which === KEY.PAGE_DOWN) {
2137                    // prevent the page from scrolling
2138                    killEvent(e);
2139                    return;
2140                }
2141
2142                switch (e.which) {
2143                    case KEY.UP:
2144                    case KEY.DOWN:
2145                        this.moveHighlight((e.which === KEY.UP) ? -1 : 1);
2146                        killEvent(e);
2147                        return;
2148                    case KEY.ENTER:
2149                        this.selectHighlighted();
2150                        killEvent(e);
2151                        return;
2152                    case KEY.TAB:
2153                        this.selectHighlighted({noFocus: true});
2154                        return;
2155                    case KEY.ESC:
2156                        this.cancel(e);
2157                        killEvent(e);
2158                        return;
2159                }
2160            }));
2161
2162            this.search.on("blur", this.bind(function(e) {
2163                // a workaround for chrome to keep the search field focussed when the scroll bar is used to scroll the dropdown.
2164                // without this the search field loses focus which is annoying
2165                if (document.activeElement === this.body.get(0)) {
2166                    window.setTimeout(this.bind(function() {
2167                        if (this.opened()) {
2168                            this.search.focus();
2169                        }
2170                    }), 0);
2171                }
2172            }));
2173
2174            this.focusser.on("keydown", this.bind(function (e) {
2175                if (!this.isInterfaceEnabled()) return;
2176
2177                if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.ESC) {
2178                    return;
2179                }
2180
2181                if (this.opts.openOnEnter === false && e.which === KEY.ENTER) {
2182                    killEvent(e);
2183                    return;
2184                }
2185
2186                if (e.which == KEY.DOWN || e.which == KEY.UP
2187                    || (e.which == KEY.ENTER && this.opts.openOnEnter)) {
2188
2189                    if (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) return;
2190
2191                    this.open();
2192                    killEvent(e);
2193                    return;
2194                }
2195
2196                if (e.which == KEY.DELETE || e.which == KEY.BACKSPACE) {
2197                    if (this.opts.allowClear) {
2198                        this.clear();
2199                    }
2200                    killEvent(e);
2201                    return;
2202                }
2203            }));
2204
2205
2206            installKeyUpChangeEvent(this.focusser);
2207            this.focusser.on("keyup-change input", this.bind(function(e) {
2208                if (this.opts.minimumResultsForSearch >= 0) {
2209                    e.stopPropagation();
2210                    if (this.opened()) return;
2211                    this.open();
2212                }
2213            }));
2214
2215            selection.on("mousedown touchstart", "abbr", this.bind(function (e) {
2216                if (!this.isInterfaceEnabled()) return;
2217                this.clear();
2218                killEventImmediately(e);
2219                this.close();
2220                this.selection.focus();
2221            }));
2222
2223            selection.on("mousedown touchstart", this.bind(function (e) {
2224                // Prevent IE from generating a click event on the body
2225                reinsertElement(selection);
2226
2227                if (!this.container.hasClass("select2-container-active")) {
2228                    this.opts.element.trigger($.Event("select2-focus"));
2229                }
2230
2231                if (this.opened()) {
2232                    this.close();
2233                } else if (this.isInterfaceEnabled()) {
2234                    this.open();
2235                }
2236
2237                killEvent(e);
2238            }));
2239
2240            dropdown.on("mousedown touchstart", this.bind(function() {
2241                if (this.opts.shouldFocusInput(this)) {
2242                    this.search.focus();
2243                }
2244            }));
2245
2246            selection.on("focus", this.bind(function(e) {
2247                killEvent(e);
2248            }));
2249
2250            this.focusser.on("focus", this.bind(function(){
2251                if (!this.container.hasClass("select2-container-active")) {
2252                    this.opts.element.trigger($.Event("select2-focus"));
2253                }
2254                this.container.addClass("select2-container-active");
2255            })).on("blur", this.bind(function() {
2256                if (!this.opened()) {
2257                    this.container.removeClass("select2-container-active");
2258                    this.opts.element.trigger($.Event("select2-blur"));
2259                }
2260            }));
2261            this.search.on("focus", this.bind(function(){
2262                if (!this.container.hasClass("select2-container-active")) {
2263                    this.opts.element.trigger($.Event("select2-focus"));
2264                }
2265                this.container.addClass("select2-container-active");
2266            }));
2267
2268            this.initContainerWidth();
2269            this.opts.element.addClass("select2-offscreen");
2270            this.setPlaceholder();
2271
2272        },
2273
2274        // single
2275        clear: function(triggerChange) {
2276            var data=this.selection.data("select2-data");
2277            if (data) { // guard against queued quick consecutive clicks
2278                var evt = $.Event("select2-clearing");
2279                this.opts.element.trigger(evt);
2280                if (evt.isDefaultPrevented()) {
2281                    return;
2282                }
2283                var placeholderOption = this.getPlaceholderOption();
2284                this.opts.element.val(placeholderOption ? placeholderOption.val() : "");
2285                this.selection.find(".select2-chosen").empty();
2286                this.selection.removeData("select2-data");
2287                this.setPlaceholder();
2288
2289                if (triggerChange !== false){
2290                    this.opts.element.trigger({ type: "select2-removed", val: this.id(data), choice: data });
2291                    this.triggerChange({removed:data});
2292                }
2293            }
2294        },
2295
2296        /**
2297         * Sets selection based on source element's value
2298         */
2299        // single
2300        initSelection: function () {
2301            var selected;
2302            if (this.isPlaceholderOptionSelected()) {
2303                this.updateSelection(null);
2304                this.close();
2305                this.setPlaceholder();
2306            } else {
2307                var self = this;
2308                this.opts.initSelection.call(null, this.opts.element, function(selected){
2309                    if (selected !== undefined && selected !== null) {
2310                        self.updateSelection(selected);
2311                        self.close();
2312                        self.setPlaceholder();
2313                        self.nextSearchTerm = self.opts.nextSearchTerm(selected, self.search.val());
2314                    }
2315                });
2316            }
2317        },
2318
2319        isPlaceholderOptionSelected: function() {
2320            var placeholderOption;
2321            if (this.getPlaceholder() === undefined) return false; // no placeholder specified so no option should be considered
2322            return ((placeholderOption = this.getPlaceholderOption()) !== undefined && placeholderOption.prop("selected"))
2323                || (this.opts.element.val() === "")
2324                || (this.opts.element.val() === undefined)
2325                || (this.opts.element.val() === null);
2326        },
2327
2328        // single
2329        prepareOpts: function () {
2330            var opts = this.parent.prepareOpts.apply(this, arguments),
2331                self=this;
2332
2333            if (opts.element.get(0).tagName.toLowerCase() === "select") {
2334                // install the selection initializer
2335                opts.initSelection = function (element, callback) {
2336                    var selected = element.find("option").filter(function() { return this.selected && !this.disabled });
2337                    // a single select box always has a value, no need to null check 'selected'
2338                    callback(self.optionToData(selected));
2339                };
2340            } else if ("data" in opts) {
2341                // install default initSelection when applied to hidden input and data is local
2342                opts.initSelection = opts.initSelection || function (element, callback) {
2343                    var id = element.val();
2344                    //search in data by id, storing the actual matching item
2345                    var match = null;
2346                    opts.query({
2347                        matcher: function(term, text, el){
2348                            var is_match = equal(id, opts.id(el));
2349                            if (is_match) {
2350                                match = el;
2351                            }
2352                            return is_match;
2353                        },
2354                        callback: !$.isFunction(callback) ? $.noop : function() {
2355                            callback(match);
2356                        }
2357                    });
2358                };
2359            }
2360
2361            return opts;
2362        },
2363
2364        // single
2365        getPlaceholder: function() {
2366            // if a placeholder is specified on a single select without a valid placeholder option ignore it
2367            if (this.select) {
2368                if (this.getPlaceholderOption() === undefined) {
2369                    return undefined;
2370                }
2371            }
2372
2373            return this.parent.getPlaceholder.apply(this, arguments);
2374        },
2375
2376        // single
2377        setPlaceholder: function () {
2378            var placeholder = this.getPlaceholder();
2379
2380            if (this.isPlaceholderOptionSelected() && placeholder !== undefined) {
2381
2382                // check for a placeholder option if attached to a select
2383                if (this.select && this.getPlaceholderOption() === undefined) return;
2384
2385                this.selection.find(".select2-chosen").html(this.opts.escapeMarkup(placeholder));
2386
2387                this.selection.addClass("select2-default");
2388
2389                this.container.removeClass("select2-allowclear");
2390            }
2391        },
2392
2393        // single
2394        postprocessResults: function (data, initial, noHighlightUpdate) {
2395            var selected = 0, self = this, showSearchInput = true;
2396
2397            // find the selected element in the result list
2398
2399            this.findHighlightableChoices().each2(function (i, elm) {
2400                if (equal(self.id(elm.data("select2-data")), self.opts.element.val())) {
2401                    selected = i;
2402                    return false;
2403                }
2404            });
2405
2406            // and highlight it
2407            if (noHighlightUpdate !== false) {
2408                if (initial === true && selected >= 0) {
2409                    this.highlight(selected);
2410                } else {
2411                    this.highlight(0);
2412                }
2413            }
2414
2415            // hide the search box if this is the first we got the results and there are enough of them for search
2416
2417            if (initial === true) {
2418                var min = this.opts.minimumResultsForSearch;
2419                if (min >= 0) {
2420                    this.showSearch(countResults(data.results) >= min);
2421                }
2422            }
2423        },
2424
2425        // single
2426        showSearch: function(showSearchInput) {
2427            if (this.showSearchInput === showSearchInput) return;
2428
2429            this.showSearchInput = showSearchInput;
2430
2431            this.dropdown.find(".select2-search").toggleClass("select2-search-hidden", !showSearchInput);
2432            this.dropdown.find(".select2-search").toggleClass("select2-offscreen", !showSearchInput);
2433            //add "select2-with-searchbox" to the container if search box is shown
2434            $(this.dropdown, this.container).toggleClass("select2-with-searchbox", showSearchInput);
2435        },
2436
2437        // single
2438        onSelect: function (data, options) {
2439
2440            if (!this.triggerSelect(data)) { return; }
2441
2442            var old = this.opts.element.val(),
2443                oldData = this.data();
2444
2445            this.opts.element.val(this.id(data));
2446            this.updateSelection(data);
2447
2448            this.opts.element.trigger({ type: "select2-selected", val: this.id(data), choice: data });
2449
2450            this.nextSearchTerm = this.opts.nextSearchTerm(data, this.search.val());
2451            this.close();
2452
2453            if ((!options || !options.noFocus) && this.opts.shouldFocusInput(this)) {
2454                this.focusser.focus();
2455            }
2456
2457            if (!equal(old, this.id(data))) {
2458                this.triggerChange({ added: data, removed: oldData });
2459            }
2460        },
2461
2462        // single
2463        updateSelection: function (data) {
2464
2465            var container=this.selection.find(".select2-chosen"), formatted, cssClass;
2466
2467            this.selection.data("select2-data", data);
2468
2469            container.empty();
2470            if (data !== null) {
2471                formatted=this.opts.formatSelection(data, container, this.opts.escapeMarkup);
2472            }
2473            if (formatted !== undefined) {
2474                container.append(formatted);
2475            }
2476            cssClass=this.opts.formatSelectionCssClass(data, container);
2477            if (cssClass !== undefined) {
2478                container.addClass(cssClass);
2479            }
2480
2481            this.selection.removeClass("select2-default");
2482
2483            if (this.opts.allowClear && this.getPlaceholder() !== undefined) {
2484                this.container.addClass("select2-allowclear");
2485            }
2486        },
2487
2488        // single
2489        val: function () {
2490            var val,
2491                triggerChange = false,
2492                data = null,
2493                self = this,
2494                oldData = this.data();
2495
2496            if (arguments.length === 0) {
2497                return this.opts.element.val();
2498            }
2499
2500            val = arguments[0];
2501
2502            if (arguments.length > 1) {
2503                triggerChange = arguments[1];
2504            }
2505
2506            if (this.select) {
2507                this.select
2508                    .val(val)
2509                    .find("option").filter(function() { return this.selected }).each2(function (i, elm) {
2510                        data = self.optionToData(elm);
2511                        return false;
2512                    });
2513                this.updateSelection(data);
2514                this.setPlaceholder();
2515                if (triggerChange) {
2516                    this.triggerChange({added: data, removed:oldData}
2516);
2517                }
2518            } else {
2519                // val is an id. !val is true for [undefined,null,'',0] - 0 is legal
2520                if (!val && val !== 0) {
2521                    this.clear(triggerChange);
2522                    return;
2523                }
2524                if (this.opts.initSelection === undefined) {
2525                    throw new Error("cannot call val() if initSelection() is not defined");
2526                }
2527                this.opts.element.val(val);
2528                this.opts.initSelection(this.opts.element, function(data){
2529                    self.opts.element.val(!data ? "" : self.id(data));
2530                    self.updateSelection(data);
2531                    self.setPlaceholder();
2532                    if (triggerChange) {
2533                        self.triggerChange({added: data, removed:oldData});
2534                    }
2535                });
2536            }
2537        },
2538
2539        // single
2540        clearSearch: function () {
2541            this.search.val("");
2542            this.focusser.val("");
2543        },
2544
2545        // single
2546        data: function(value) {
2547            var data,
2548                triggerChange = false;
2549
2550            if (arguments.length === 0) {
2551                data = this.selection.data("select2-data");
2552                if (data == undefined) data = null;
2553                return data;
2554            } else {
2555                if (arguments.length > 1) {
2556                    triggerChange = arguments[1];
2557                }
2558                if (!value) {
2559                    this.clear(triggerChange);
2560                } else {
2561                    data = this.data();
2562                    this.opts.element.val(!value ? "" : this.id(value));
2563                    this.updateSelection(value);
2564                    if (triggerChange) {
2565                        this.triggerChange({added: value, removed:data});
2566                    }
2567                }
2568            }
2569        }
2570    });
2571
2572    MultiSelect2 = clazz(AbstractSelect2, {
2573
2574        // multi
2575        createContainer: function (opts) {
2576            var container = $(document.createElement("div")).attr({
2577                "class": "select2-container select2-container-multi"
2578            }).html([
2579                "<ul class='select2-choices'>",
2580                "  <li class='select2-search-field'>",
2581                "    <label for='' class='select2-offscreen'></label>",
2582                "    <input type='text' autocomplete='off' autocorrect='off' autocapitalize='off' spellcheck='false' class='select2-input'>",
2583                "  </li>",
2584                "</ul>",
2585                "<div class='select2-drop select2-drop-multi select2-display-none'>",
2586                "   <ul class='select2-results'>",
2587                "   </ul>",
2588                "</div>"].join(""));
2589            return container;
2590        },
2591
2592        // multi
2593        prepareOpts: function () {
2594            var opts = this.parent.prepareOpts.apply(this, arguments),
2595                self=this;
2596
2597            // TODO validate placeholder is a string if specified
2598
2599            if (opts.element.get(0).tagName.toLowerCase() === "select") {
2600                // install the selection initializer
2601                opts.initSelection = function (element, callback) {
2602
2603                    var data = [];
2604
2605                    element.find("option").filter(function() { return this.selected && !this.disabled }).each2(function (i, elm) {
2606                        data.push(self.optionToData(elm));
2607                    });
2608                    callback(data);
2609                };
2610            } else if ("data" in opts) {
2611                // install default initSelection when applied to hidden input and data is local
2612                opts.initSelection = opts.initSelection || function (element, callback) {
2613                    var ids = splitVal(element.val(), opts.separator);
2614                    //search in data by array of ids, storing matching items in a list
2615                    var matches = [];
2616                    opts.query({
2617                        matcher: function(term, text, el){
2618                            var is_match = $.grep(ids, function(id) {
2619                                return equal(id, opts.id(el));
2620                            }).length;
2621                            if (is_match) {
2622                                matches.push(el);
2623                            }
2624                            return is_match;
2625                        },
2626                        callback: !$.isFunction(callback) ? $.noop : function() {
2627                            // reorder matches based on the order they appear in the ids array because right now
2628                            // they are in the order in which they appear in data array
2629                            var ordered = [];
2630                            for (var i = 0; i < ids.length; i++) {
2631                                var id = ids[i];
2632                                for (var j = 0; j < matches.length; j++) {
2633                                    var match = matches[j];
2634                                    if (equal(id, opts.id(match))) {
2635                                        ordered.push(match);
2636                                        matches.splice(j, 1);
2637                                        break;
2638                                    }
2639                                }
2640                            }
2641                            callback(ordered);
2642                        }
2643                    });
2644                };
2645            }
2646
2647            return opts;
2648        },
2649
2650        // multi
2651        selectChoice: function (choice) {
2652
2653            var selected = this.container.find(".select2-search-choice-focus");
2654            if (selected.length && choice && choice[0] == selected[0]) {
2655
2656            } else {
2657                if (selected.length) {
2658                    this.opts.element.trigger("choice-deselected", selected);
2659                }
2660                selected.removeClass("select2-search-choice-focus");
2661                if (choice && choice.length) {
2662                    this.close();
2663                    choice.addClass("select2-search-choice-focus");
2664                    this.opts.element.trigger("choice-selected", choice);
2665                }
2666            }
2667        },
2668
2669        // multi
2670        destroy: function() {
2671            $("label[for='" + this.search.attr('id') + "']")
2672                .attr('for', this.opts.element.attr("id"));
2673            this.parent.destroy.apply(this, arguments);
2674
2675            cleanupJQueryElements.call(this,
2676                "searchContainer",
2677                "selection"
2678            );
2679        },
2680
2681        // multi
2682        initContainer: function () {
2683
2684            var selector = ".select2-choices", selection;
2685
2686            this.searchContainer = this.container.find(".select2-search-field");
2687            this.selection = selection = this.container.find(selector);
2688
2689            var _this = this;
2690            this.selection.on("click", ".select2-search-choice:not(.select2-locked)", function (e) {
2691                //killEvent(e);
2692                _this.search[0].focus();
2693                _this.selectChoice($(this));
2694            });
2695
2696            // rewrite labels from original element to focusser
2697            this.search.attr("id", "s2id_autogen"+nextUid());
2698
2699            this.search.prev()
2700                .text($("label[for='" + this.opts.element.attr("id") + "']").text())
2701                .attr('for', this.search.attr('id'));
2702
2703            this.search.on("input paste", this.bind(function() {
2704                if (this.search.attr('placeholder') && this.search.val().length == 0) return;
2705                if (!this.isInterfaceEnabled()) return;
2706                if (!this.opened()) {
2707                    this.open();
2708                }
2709            }));
2710
2711            this.search.attr("tabindex", this.elementTabIndex);
2712
2713            this.keydowns = 0;
2714            this.search.on("keydown", this.bind(function (e) {
2715                if (!this.isInterfaceEnabled()) return;
2716
2717                ++this.keydowns;
2718                var selected = selection.find(".select2-search-choice-focus");
2719                var prev = selected.prev(".select2-search-choice:not(.select2-locked)");
2720                var next = selected.next(".select2-search-choice:not(.select2-locked)");
2721                var pos = getCursorInfo(this.search);
2722
2723                if (selected.length &&
2724                    (e.which == KEY.LEFT || e.which == KEY.RIGHT || e.which == KEY.BACKSPACE || e.which == KEY.DELETE || e.which == KEY.ENTER)) {
2725                    var selectedChoice = selected;
2726                    if (e.which == KEY.LEFT && prev.length) {
2727                        selectedChoice = prev;
2728                    }
2729                    else if (e.which == KEY.RIGHT) {
2730                        selectedChoice = next.length ? next : null;
2731                    }
2732                    else if (e.which === KEY.BACKSPACE) {
2733                        if (this.unselect(selected.first())) {
2734                            this.search.width(10);
2735                            selectedChoice = prev.length ? prev : next;
2736                        }
2737                    } else if (e.which == KEY.DELETE) {
2738                        if (this.unselect(selected.first())) {
2739                            this.search.width(10);
2740                            selectedChoice = next.length ? next : null;
2741                        }
2742                    } else if (e.which == KEY.ENTER) {
2743                        selectedChoice = null;
2744                    }
2745
2746                    this.selectChoice(selectedChoice);
2747                    killEvent(e);
2748                    if (!selectedChoice || !selectedChoice.length) {
2749                        this.open();
2750                    }
2751                    return;
2752                } else if (((e.which === KEY.BACKSPACE && this.keydowns == 1)
2753                    || e.which == KEY.LEFT) && (pos.offset == 0 && !pos.length)) {
2754
2755                    this.selectChoice(selection.find(".select2-search-choice:not(.select2-locked)").last());
2756                    killEvent(e);
2757                    return;
2758                } else {
2759                    this.selectChoice(null);
2760                }
2761
2762                if (this.opened()) {
2763                    switch (e.which) {
2764                    case KEY.UP:
2765                    case KEY.DOWN:
2766                        this.moveHighlight((e.which === KEY.UP) ? -1 : 1);
2767                        killEvent(e);
2768                        return;
2769                    case KEY.ENTER:
2770                        this.selectHighlighted();
2771                        killEvent(e);
2772                        return;
2773                    case KEY.TAB:
2774                        this.selectHighlighted({noFocus:true});
2775                        this.close();
2776                        return;
2777                    case KEY.ESC:
2778                        this.cancel(e);
2779                        killEvent(e);
2780                        return;
2781                    }
2782                }
2783
2784                if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e)
2785                 || e.which === KEY.BACKSPACE || e.which === KEY.ESC) {
2786                    return;
2787                }
2788
2789                if (e.which === KEY.ENTER) {
2790                    if (this.opts.openOnEnter === false) {
2791                        return;
2792                    } else if (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) {
2793                        return;
2794                    }
2795                }
2796
2797                this.open();
2798
2799                if (e.which === KEY.PAGE_UP || e.which === KEY.PAGE_DOWN) {
2800                    // prevent the page from scrolling
2801                    killEvent(e);
2802                }
2803
2804                if (e.which === KEY.ENTER) {
2805                    // prevent form from being submitted
2806                    killEvent(e);
2807                }
2808
2809            }));
2810
2811            this.search.on("keyup", this.bind(function (e) {
2812                this.keydowns = 0;
2813                this.resizeSearch();
2814            })
2815            );
2816
2817            this.search.on("blur", this.bind(function(e) {
2818                this.container.removeClass("select2-container-active");
2819                this.search.removeClass("select2-focused");
2820                this.selectChoice(null);
2821                if (!this.opened()) this.clearSearch();
2822                e.stopImmediatePropagation();
2823                this.opts.element.trigger($.Event("select2-blur"));
2824            }));
2825
2826            this.container.on("click", selector, this.bind(function (e) {
2827                if (!this.isInterfaceEnabled()) return;
2828                if ($(e.target).closest(".select2-search-choice").length > 0) {
2829                    // clicked inside a select2 search choice, do not open
2830                    return;
2831                }
2832                this.selectChoice(null);
2833                this.clearPlaceholder();
2834                if (!this.container.hasClass("select2-container-active")) {
2835                    this.opts.element.trigger($.Event("select2-focus"));
2836                }
2837                this.open();
2838                this.focusSearch();
2839                e.preventDefault();
2840            }));
2841
2842            this.container.on("focus", selector, this.bind(function () {
2843                if (!this.isInterfaceEnabled()) return;
2844                if (!this.container.hasClass("select2-container-active")) {
2845                    this.opts.element.trigger($.Event("select2-focus"));
2846                }
2847                this.container.addClass("select2-container-active");
2848                this.dropdown.addClass("select2-drop-active");
2849                this.clearPlaceholder();
2850            }));
2851
2852            this.initContainerWidth();
2853            this.opts.element.addClass("select2-offscreen");
2854
2855            // set the placeholder if necessary
2856            this.clearSearch();
2857        },
2858
2859        // multi
2860        enableInterface: function() {
2861            if (this.parent.enableInterface.apply(this, arguments)) {
2862                this.search.prop("disabled", !this.isInterfaceEnabled());
2863            }
2864        },
2865
2866        // multi
2867        initSelection: function () {
2868            var data;
2869            if (this.opts.element.val() === "" && this.opts.element.text() === "") {
2870                this.updateSelection([]);
2871                this.close();
2872                // set the placeholder if necessary
2873                this.clearSearch();
2874            }
2875            if (this.select || this.opts.element.val() !== "") {
2876                var self = this;
2877                this.opts.initSelection.call(null, this.opts.element, function(data){
2878                    if (data !== undefined && data !== null) {
2879                        self.updateSelection(data);
2880                        self.close();
2881                        // set the placeholder if necessary
2882                        self.clearSearch();
2883                    }
2884                });
2885            }
2886        },
2887
2888        // multi
2889        clearSearch: function () {
2890            var placeholder = this.getPlaceholder(),
2891                maxWidth = this.getMaxSearchWidth();
2892
2893            if (placeholder !== undefined  && this.getVal().length === 0 && this.search.hasClass("select2-focused") === false) {
2894                this.search.val(placeholder).addClass("select2-default");
2895                // stretch the search box to full width of the container so as much of the placeholder is visible as possible
2896                // 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
2897                this.search.width(maxWidth > 0 ? maxWidth : this.container.css("width"));
2898            } else {
2899                this.search.val("").width(10);
2900            }
2901        },
2902
2903        // multi
2904        clearPlaceholder: function () {
2905            if (this.search.hasClass("select2-default")) {
2906                this.search.val("").removeClass("select2-default");
2907            }
2908        },
2909
2910        // multi
2911        opening: function () {
2912            this.clearPlaceholder(); // should be done before super so placeholder is not used to search
2913            this.resizeSearch();
2914
2915            this.parent.opening.apply(this, arguments);
2916
2917            this.focusSearch();
2918
2919            // initializes search's value with nextSearchTerm (if defined by user)
2920            // ignore nextSearchTerm if the dropdown is opened by the user pressing a letter
2921            if(this.search.val() === "") {
2922                if(this.nextSearchTerm != undefined){
2923                    this.search.val(this.nextSearchTerm);
2924                    this.search.select();
2925                }
2926            }
2927
2928            this.updateResults(true);
2929            if (this.opts.shouldFocusInput(this)) {
2930                this.search.focus();
2931            }
2932            this.opts.element.trigger($.Event("select2-open"));
2933        },
2934
2935        // multi
2936        close: function () {
2937            if (!this.opened()) return;
2938            this.parent.close.apply(this, arguments);
2939        },
2940
2941        // multi
2942        focus: function () {
2943            this.close();
2944            this.search.focus();
2945        },
2946
2947        // multi
2948        isFocused: function () {
2949            return this.search.hasClass("select2-focused");
2950        },
2951
2952        // multi
2953        updateSelection: function (data) {
2954            var ids = [], filtered = [], self = this;
2955
2956            // filter out duplicates
2957            $(data).each(function () {
2958                if (indexOf(self.id(this), ids) < 0) {
2959                    ids.push(self.id(this));
2960                    filtered.push(this);
2961                }
2962            });
2963            data = filtered;
2964
2965            this.selection.find(".select2-search-choice").remove();
2966            $(data).each(function () {
2967                self.addSelectedChoice(this);
2968            });
2969            self.postprocessResults();
2970        },
2971
2972        // multi
2973        tokenize: function() {
2974            var input = this.search.val();
2975            input = this.opts.tokenizer.call(this, input, this.data(), this.bind(this.onSelect), this.opts);
2976            if (input != null && input != undefined) {
2977                this.search.val(input);
2978                if (input.length > 0) {
2979                    this.open();
2980                }
2981            }
2982
2983        },
2984
2985        // multi
2986        onSelect: function (data, options) {
2987
2988            if (!this.triggerSelect(data) || data.text === "") { return; }
2989
2990            this.addSelectedChoice(data);
2991
2992            this.opts.element.trigger({ type: "selected", val: this.id(data), choice: data });
2993
2994            // keep track of the search's value before it gets cleared
2995            this.nextSearchTerm = this.opts.nextSearchTerm(data, this.search.val());
2996
2997            this.clearSearch();
2998            this.updateResults();
2999
3000            if (this.select || !this.opts.closeOnSelect) this.postprocessResults(data, false, this.opts.closeOnSelect===true);
3001
3002            if (this.opts.closeOnSelect) {
3003                this.close();
3004                this.search.width(10);
3005            } else {
3006                if (this.countSelectableResults()>0) {
3007                    this.search.width(10);
3008                    this.resizeSearch();
3009                    if (this.getMaximumSelectionSize() > 0 && this.val().length >= this.getMaximumSelectionSize()) {
3010                        // if we reached max selection size repaint the results so choices
3011                        // are replaced with the max selection reached message
3012                        this.updateResults(true);
3013                    } else {
3014                        // initializes search's value with nextSearchTerm and update search result
3015                        if(this.nextSearchTerm != undefined){
3016                            this.search.val(this.nextSearchTerm);
3017                            this.updateResults();
3018                            this.search.select();
3019                        }
3020                    }
3021                    this.positionDropdown();
3022                } else {
3023                    // if nothing left to select close
3024                    this.close();
3025                    this.search.width(10);
3026                }
3027            }
3028
3029            // since its not possible to select an element that has already been
3030            // added we do not need to check if this is a new element before firing change
3031            this.triggerChange({ added: data });
3032
3033            if (!options || !options.noFocus)
3034                this.focusSearch();
3035        },
3036
3037        // multi
3038        cancel: function () {
3039            this.close();
3040            this.focusSearch();
3041        },
3042
3043        addSelectedChoice: function (data) {
3044            var enableChoice = !data.locked,
3045                enabledItem = $(
3046                    "<li class='select2-search-choice'>" +
3047                    "    <div></div>" +
3048                    "    <a href='#' class='select2-search-choice-close' tabindex='-1'></a>" +
3049                    "</li>"),
3050                disabledItem = $(
3051                    "<li class='select2-search-choice select2-locked'>" +
3052                    "<div></div>" +
3053                    "</li>");
3054            var choice = enableChoice ? enabledItem : disabledItem,
3055                id = this.id(data),
3056                val = this.getVal(),
3057                formatted,
3058                cssClass;
3059
3060            formatted=this.opts.formatSelection(data, choice.find("div"), this.opts.escapeMarkup);
3061            if (formatted != undefined) {
3062                choice.find("div").replaceWith("<div>"+formatted+"</div>");
3063            }
3064            cssClass=this.opts.formatSelectionCssClass(data, choice.find("div"));
3065            if (cssClass != undefined) {
3066                choice.addClass(cssClass);
3067            }
3068
3069            if(enableChoice){
3070              choice.find(".select2-search-choice-close")
3071                  .on("mousedown", killEvent)
3072                  .on("click dblclick", this.bind(function (e) {
3073                  if (!this.isInterfaceEnabled()) return;
3074
3075                  this.unselect($(e.target));
3076                  this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus");
3077                  killEvent(e);
3078                  this.close();
3079                  this.focusSearch();
3080              })).on("focus", this.bind(function () {
3081                  if (!this.isInterfaceEnabled()) return;
3082                  this.container.addClass("select2-container-active");
3083                  this.dropdown.addClass("select2-drop-active");
3084              }));
3085            }
3086
3087            choice.data("select2-data", data);
3088            choice.insertBefore(this.searchContainer);
3089
3090            val.push(id);
3091            this.setVal(val);
3092        },
3093
3094        // multi
3095        unselect: function (selected) {
3096            var val = this.getVal(),
3097                data,
3098                index;
3099            selected = selected.closest(".select2-search-choice");
3100
3101            if (selected.length === 0) {
3102                throw "Invalid argument: " + selected + ". Must be .select2-search-choice";
3103            }
3104
3105            data = selected.data("select2-data");
3106
3107            if (!data) {
3108                // prevent a race condition when the 'x' is clicked really fast repeatedly the event can be queued
3109                // and invoked on an element already removed
3110                return;
3111            }
3112
3113            var evt = $.Event("select2-removing");
3114            evt.val = this.id(data);
3115            evt.choice = data;
3116            this.opts.element.trigger(evt);
3117
3118            if (evt.isDefaultPrevented()) {
3119                return false;
3120            }
3121
3122            while((index = indexOf(this.id(data), val)) >= 0) {
3123                val.splice(index, 1);
3124                this.setVal(val);
3125                if (this.select) this.postprocessResults();
3126            }
3127
3128            selected.remove();
3129
3130            this.opts.element.trigger({ type: "select2-removed", val: this.id(data), choice: data });
3131            this.triggerChange({ removed: data });
3132
3133            return true;
3134        },
3135
3136        // multi
3137        postprocessResults: function (data, initial, noHighlightUpdate) {
3138            var val = this.getVal(),
3139                choices = this.results.find(".select2-result"),
3140                compound = this.results.find(".select2-result-with-children"),
3141                self = this;
3142
3143            choices.each2(function (i, choice) {
3144                var id = self.id(choice.data("select2-data"));
3145                if (indexOf(id, val) >= 0) {
3146                    choice.addClass("select2-selected");
3147                    // mark all children of the selected parent as selected
3148                    choice.find(".select2-result-selectable").addClass("select2-selected");
3149                }
3150            });
3151
3152            compound.each2(function(i, choice) {
3153                // hide an optgroup if it doesn't have any selectable children
3154                if (!choice.is('.select2-result-selectable')
3155                    && choice.find(".select2-result-selectable:not(.select2-selected)").length === 0) {
3156                    choice.addClass("select2-selected");
3157                }
3158            });
3159
3160            if (this.highlight() == -1 && noHighlightUpdate !== false){
3161                self.highlight(0);
3162            }
3163
3164            //If all results are chosen render formatNoMatches
3165            if(!this.opts.createSearchChoice && !choices.filter('.select2-result:not(.select2-selected)').length > 0){
3166                if(!data || data && !data.more && this.results.find(".select2-no-results").length === 0) {
3167                    if (checkFormatter(self.opts.formatNoMatches, "formatNoMatches")) {
3168                        this.results.append("<li class='select2-no-results'>" + evaluate(self.opts.formatNoMatches, self.opts.element, self.search.val()) + "</li>");
3169                    }
3170                }
3171            }
3172
3173        },
3174
3175        // multi
3176        getMaxSearchWidth: function() {
3177            return this.selection.width() - getSideBorderPadding(this.search);
3178        },
3179
3180        // multi
3181        resizeSearch: function () {
3182            var minimumWidth, left, maxWidth, containerLeft, searchWidth,
3183                sideBorderPadding = getSideBorderPadding(this.search);
3184
3185            minimumWidth = measureTextWidth(this.search) + 10;
3186
3187            left = this.search.offset().left;
3188
3189            maxWidth = this.selection.width();
3190            containerLeft = this.selection.offset().left;
3191
3192            searchWidth = maxWidth - (left - containerLeft) - sideBorderPadding;
3193
3194            if (searchWidth < minimumWidth) {
3195                searchWidth = maxWidth - sideBorderPadding;
3196            }
3197
3198            if (searchWidth < 40) {
3199                searchWidth = maxWidth - sideBorderPadding;
3200            }
3201
3202            if (searchWidth <= 0) {
3203              searchWidth = minimumWidth;
3204            }
3205
3206            this.search.width(Math.floor(searchWidth));
3207        },
3208
3209        // multi
3210        getVal: function () {
3211            var val;
3212            if (this.select) {
3213                val = this.select.val();
3214                return val === null ? [] : val;
3215            } else {
3216                val = this.opts.element.val();
3217                return splitVal(val, this.opts.separator);
3218            }
3219        },
3220
3221        // multi
3222        setVal: function (val) {
3223            var unique;
3224            if (this.select) {
3225                this.select.val(val);
3226            } else {
3227                unique = [];
3228                // filter out duplicates
3229                $(val).each(function () {
3230                    if (indexOf(this, unique) < 0) unique.push(this);
3231                });
3232                this.opts.element.val(unique.length === 0 ? "" : unique.join(this.opts.separator));
3233            }
3234        },
3235
3236        // multi
3237        buildChangeDetails: function (old, current) {
3238            var current = current.slice(0),
3239                old = old.slice(0);
3240
3241            // remove intersection from each array
3242            for (var i = 0; i < current.length; i++) {
3243                for (var j = 0; j < old.length; j++) {
3244                    if (equal(this.opts.id(current[i]), this.opts.id(old[j]))) {
3245                        current.splice(i, 1);
3246                        if(i>0){
3247                        	i--;
3248                        }
3249                        old.splice(j, 1);
3250                        j--;
3251                    }
3252                }
3253            }
3254
3255            return {added: current, removed: old};
3256        },
3257
3258
3259        // multi
3260        val: function (val, triggerChange) {
3261            var oldData, self=this;
3262
3263            if (arguments.length === 0) {
3264                return this.getVal();
3265            }
3266
3267            oldData=this.data();
3268            if (!oldData.length) oldData=[];
3269
3270            // val is an id. !val is true for [undefined,null,'',0] - 0 is legal
3271            if (!val && val !== 0) {
3272                this.opts.element.val("");
3273                this.updateSelection([]);
3274                this.clearSearch();
3275                if (triggerChange) {
3276                    this.triggerChange({added: this.data(), removed: oldData});
3277                }
3278                return;
3279            }
3280
3281            // val is a list of ids
3282            this.setVal(val);
3283
3284            if (this.select) {
3285                this.opts.initSelection(this.select, this.bind(this.updateSelection));
3286                if (triggerChange) {
3287                    this.triggerChange(this.buildChangeDetails(oldData, this.data()));
3288                }
3289            } else {
3290                if (this.opts.initSelection === undefined) {
3291                    throw new Error("val() cannot be called if initSelection() is not defined");
3292                }
3293
3294                this.opts.initSelection(this.opts.element, function(data){
3295                    var ids=$.map(data, self.id);
3296                    self.setVal(ids);
3297                    self.updateSelection(data);
3298                    self.clearSearch();
3299                    if (triggerChange) {
3300                        self.triggerChange(self.buildChangeDetails(oldData, self.data()));
3301                    }
3302                });
3303            }
3304            this.clearSearch();
3305        },
3306
3307        // multi
3308        onSortStart: function() {
3309            if (this.select) {
3310                throw new Error("Sorting of elements is not supported when attached to <select>. Attach to <input type='hidden'/> instead.");
3311            }
3312
3313            // collapse search field into 0 width so its container can be collapsed as well
3314            this.search.width(0);
3315            // hide the container
3316            this.searchContainer.hide();
3317        },
3318
3319        // multi
3320        onSortEnd:function() {
3321
3322            var val=[], self=this;
3323
3324            // show search and move it to the end of the list
3325            this.searchContainer.show();
3326            // make sure the search container is the last item in the list
3327            this.searchContainer.appendTo(this.searchContainer.parent());
3328            // since we collapsed the width in dragStarted, we resize it here
3329            this.resizeSearch();
3330
3331            // update selection
3332            this.selection.find(".select2-search-choice").each(function() {
3333                val.push(self.opts.id($(this).data("select2-data")));
3334            });
3335            this.setVal(val);
3336            this.triggerChange();
3337        },
3338
3339        // multi
3340        data: function(values, triggerChange) {
3341            var self=this, ids, old;
3342            if (arguments.length === 0) {
3343                 return this.selection
3344                     .children(".select2-search-choice")
3345                     .map(function() { return $(this).data("select2-data"); })
3346                     .get();
3347            } else {
3348                old = this.data();
3349                if (!values) { values = []; }
3350                ids = $.map(values, function(e) { return self.opts.id(e); });
3351                this.setVal(ids);
3352                this.updateSelection(values);
3353                this.clearSearch();
3354                if (triggerChange) {
3355                    this.triggerChange(this.buildChangeDetails(old, this.data()));
3356                }
3357            }
3358        }
3359    });
3360
3361    $.fn.select2 = function () {
3362
3363        var args = Array.prototype.slice.call(arguments, 0),
3364            opts,
3365            select2,
3366            method, value, multiple,
3367            allowedMethods = ["val", "destroy", "opened", "open", "close", "focu
3367s", "isFocused", "container", "dropdown", "onSortStart", "onSortEnd", "enable", "disable", "readonly", "positionDropdown", "data", "search"],
3368            valueMethods = ["opened", "isFocused", "container", "dropdown"],
3369            propertyMethods = ["val", "data"],
3370            methodsMap = { search: "externalSearch" };
3371
3372        this.each(function () {
3373            if (args.length === 0 || typeof(args[0]) === "object") {
3374                opts = args.length === 0 ? {} : $.extend({}, args[0]);
3375                opts.element = $(this);
3376
3377                if (opts.element.get(0).tagName.toLowerCase() === "select") {
3378                    multiple = opts.element.prop("multiple");
3379                } else {
3380                    multiple = opts.multiple || false;
3381                    if ("tags" in opts) {opts.multiple = multiple = true;}
3382                }
3383
3384                select2 = multiple ? new window.Select2["class"].multi() : new window.Select2["class"].single();
3385                select2.init(opts);
3386            } else if (typeof(args[0]) === "string") {
3387
3388                if (indexOf(args[0], allowedMethods) < 0) {
3389                    throw "Unknown method: " + args[0];
3390                }
3391
3392                value = undefined;
3393                select2 = $(this).data("select2");
3394                if (select2 === undefined) return;
3395
3396                method=args[0];
3397
3398                if (method === "container") {
3399                    value = select2.container;
3400                } else if (method === "dropdown") {
3401                    value = select2.dropdown;
3402                } else {
3403                    if (methodsMap[method]) method = methodsMap[method];
3404
3405                    value = select2[method].apply(select2, args.slice(1));
3406                }
3407                if (indexOf(args[0], valueMethods) >= 0
3408                    || (indexOf(args[0], propertyMethods) >= 0 && args.length == 1)) {
3409                    return false; // abort the iteration, ready to return first matched value
3410                }
3411            } else {
3412                throw "Invalid arguments to select2 plugin: " + args;
3413            }
3414        });
3415        return (value === undefined) ? this : value;
3416    };
3417
3418    // plugin defaults, accessible to users
3419    $.fn.select2.defaults = {
3420        width: "copy",
3421        loadMorePadding: 0,
3422        closeOnSelect: true,
3423        openOnEnter: true,
3424        containerCss: {},
3425        dropdownCss: {},
3426        containerCssClass: "",
3427        dropdownCssClass: "",
3428        formatResult: function(result, container, query, escapeMarkup) {
3429            var markup=[];
3430            markMatch(result.text, query.term, markup, escapeMarkup);
3431            return markup.join("");
3432        },
3433        formatSelection: function (data, container, escapeMarkup) {
3434            return data ? escapeMarkup(data.text) : undefined;
3435        },
3436        sortResults: function (results, container, query) {
3437            return results;
3438        },
3439        formatResultCssClass: function(data) {return data.css;},
3440        formatSelectionCssClass: function(data, container) {return undefined;},
3441        minimumResultsForSearch: 0,
3442        minimumInputLength: 0,
3443        maximumInputLength: null,
3444        maximumSelectionSize: 0,
3445        id: function (e) { return e == undefined ? null : e.id; },
3446        matcher: function(term, text) {
3447            return stripDiacritics(''+text).toUpperCase().indexOf(stripDiacritics(''+term).toUpperCase()) >= 0;
3448        },
3449        separator: ",",
3450        tokenSeparators: [],
3451        tokenizer: defaultTokenizer,
3452        escapeMarkup: defaultEscapeMarkup,
3453        blurOnChange: false,
3454        selectOnBlur: false,
3455        searchBar: true,
3456        adaptContainerCssClass: function(c) { return c; },
3457        adaptDropdownCssClass: function(c) { return null; },
3458        nextSearchTerm: function(selectedObject, currentSearchTerm) { return undefined; },
3459        searchInputPlaceholder: '',
3460        createSearchChoicePosition: 'top',
3461        shouldFocusInput: function (instance) {
3462            // Attempt to detect touch devices
3463            var supportsTouchEvents = (('ontouchstart' in window) ||
3464                                       (navigator.msMaxTouchPoints > 0));
3465
3466            // Only devices which support touch events should be special cased
3467            if (!supportsTouchEvents) {
3468                return true;
3469            }
3470
3471            // Never focus the input if search is disabled
3472            if (instance.opts.minimumResultsForSearch < 0) {
3473                return false;
3474            }
3475
3476            return true;
3477        }
3478    };
3479
3480    $.fn.select2.locales = [];
3481
3482    $.fn.select2.locales['en'] = {
3483         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."; },
3484         formatNoMatches: function () { return "Aucun résultat"; },
3485         formatAjaxError: function (jqXHR, textStatus, errorThrown) { return "Loading failed"; },
3486         formatInputTooShort: function (input, min) { var n = min - input.length; return "Please enter " + n + " or more character" + (n == 1 ? "" : "s"); },
3487         formatInputTooLong: function (input, max) { var n = input.length - max; return "Please delete " + n + " character" + (n == 1 ? "" : "s"); },
3488         formatSelectionTooBig: function (limit) { return "You can only select " + limit + " item" + (limit == 1 ? "" : "s"); },
3489         formatLoadMore: function (pageNumber) { return "Loading more results…"; },
3490         formatSearching: function () { return "Searching…"; },
3491    };
3492
3493    $.extend($.fn.select2.defaults, $.fn.select2.locales['en']);
3494
3495    $.fn.select2.ajaxDefaults = {
3496        transport: $.ajax,
3497        params: {
3498            type: "GET",
3499            cache: false,
3500            dataType: "json"
3501        }
3502    };
3503
3504    // exports
3505    window.Select2 = {
3506        query: {
3507            ajax: ajax,
3508            local: local,
3509            tags: tags
3510        }, util: {
3511            debounce: debounce,
3512            markMatch: markMatch,
3513            escapeMarkup: defaultEscapeMarkup,
3514            stripDiacritics: stripDiacritics
3515        }, "class": {
3516            "abstract": AbstractSelect2,
3517            "single": SingleSelect2,
3518            "multi": MultiSelect2
3519        }
3520    };
3521
3522}(jQuery));

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.